View Javadoc

1   package sk.uniba.euromath.editor.textEditor;
2   
3   import java.beans.PropertyChangeEvent;
4   import java.beans.PropertyChangeListener;
5   
6   import org.eclipse.draw2d.FigureCanvas;
7   import org.eclipse.draw2d.IFigure;
8   import org.eclipse.draw2d.Viewport;
9   import org.eclipse.draw2d.geometry.Point;
10  import org.eclipse.swt.SWT;
11  import org.eclipse.swt.widgets.Canvas;
12  import org.eclipse.swt.widgets.Caret;
13  
14  import sk.baka.ikslibs.ptr.DomPointer;
15  import sk.uniba.euromath.document.XMLAccess;
16  import sk.uniba.euromath.editor.textEditor.requests.RequestConstants;
17  
18  /***
19   * Each TextEditor can have its own CaretManager. CaretManager holds an instance
20   * of Caret, shows it on Canvas and moves it. Caret is always placed in
21   * ITextPieceKeeper at character gap. Character gap is gap between characters,
22   * specialls are gaps at start and end of text. Are index from zero to length of
23   * text. First gap(index 0) is before first character(index 0), last gap(index
24   * length) is after last character(index length -1).<br>
25   * 
26   * @author Martin Kollar 3.2.2006
27   */
28  public class CaretManager {
29  
30          /***
31           * Caret instance.
32           */
33          private Caret caret;
34  
35          /***
36           * Canvas on that is caret drawn.
37           */
38          private Canvas canvas;
39  
40          /***
41           * Caret offset measured in character gaps in ITextPieceKeeper.
42           */
43          private int caretOffset;
44  
45          /***
46           * ITextPieceKeeper where is Caret shown.
47           */
48          private ITextPieceKeeper keeperUnderCaret;
49  
50          /***
51           * XMLAccess instance.
52           */
53          private XMLAccess xmlAccess;
54  
55          /***
56           * Listens to scrolling or resizeing of the window.
57           */
58          private PropertyChangeListener propertyListener;
59  
60          /***
61           * Constructor.
62           * 
63           * @param canvas
64           *                Canvas on that the Caret will be painted
65           * @param xmlAccess
66           *                The source document accessor
67           */
68          public CaretManager(Canvas canvas, XMLAccess xmlAccess) {
69                  caret = new Caret(canvas, SWT.NONE);
70                  caretOffset = -1;
71                  this.xmlAccess = xmlAccess;
72                  this.canvas = canvas;
73  
74                  propertyListener = new PropertyChangeListener() {
75  
76                          /***
77                           * When slide bars on the edges are moved or size of
78                           * windows is changed then position of Caret is adjusted
79                           * in this method
80                           */
81                          public void propertyChange(PropertyChangeEvent evt) {
82  
83                                  if (Viewport.PROPERTY_VIEW_LOCATION.equals(evt
84                                                  .getPropertyName())) {
85                                          if ((caret == null)
86                                                          || caret.isDisposed())
87                                                  return;
88                                          if (keeperUnderCaret == null)
89                                                  return;
90                                          IFigure hostFigure = keeperUnderCaret
91                                                          .getTextLocator();
92                                          if (caret.isVisible()
93                                                          && (hostFigure
94                                                                          .isShowing())
95                                                          && (getCaretOffset() != -1)) {
96  
97                                                  showCaretAtCharGap(
98                                                                  keeperUnderCaret,
99                                                                  caretOffset);
100                                         }
101                                 }
102                         }
103 
104                 };
105 
106         }
107 
108         /***
109          * Converts direction string to key code.
110          * 
111          * @param s
112          *                One of RequestConstants.LEFT[RIGHT,UP,DOWN,HOME,END]
113          * @return SWT key code of relevant key
114          */
115         public static int directionToKeyCode(String direction) {
116                 if (direction == RequestConstants.LEFT)
117                         return SWT.ARROW_LEFT;
118                 if (direction == RequestConstants.RIGHT)
119                         return SWT.ARROW_RIGHT;
120                 if (direction == RequestConstants.UP)
121                         return SWT.ARROW_UP;
122                 if (direction == RequestConstants.DOWN)
123                         return SWT.ARROW_DOWN;
124                 if (direction == RequestConstants.HOME)
125                         return SWT.HOME;
126                 if (direction == RequestConstants.END)
127                         return SWT.END;
128                 throw new IllegalStateException(
129                                 "Wrong direction string - can not convert to key code");
130         }
131 
132         /***
133          * Returns text piece keeper where caret is.
134          * 
135          * @return TextPieceKeeper or null
136          */
137         public ITextPieceKeeper getActiveTextPieceKeeper() {
138                 return this.keeperUnderCaret;
139         }
140 
141         /***
142          * see {@link Caret#getLocation() }
143          * 
144          * @return Location of the Caret or <code>null</code> if it is not
145          *         active
146          */
147         public Point getLocation() {
148                 if (checkCaret() && caret.isVisible())
149                         return new Point(caret.getLocation().x, caret
150                                         .getLocation().y);
151                 return null;
152         }
153 
154         /***
155          * 
156          * @return DOMPointer in that is Caret activated. If it is not active,
157          *         then returns <code>null</code>
158          */
159         public DomPointer getDOMPointer() {
160                 if (checkCaret() && caret.isVisible()
161                                 && (getActiveTextPieceKeeper() != null)) {
162                         return getActiveTextPieceKeeper().getTextPieceInfo()
163                                         .getDomPointer(this.caretOffset,
164                                                         this.xmlAccess);
165 
166                 }
167                 return null;
168         }
169 
170         /***
171          * Returns index of character gap, where caret is shown.
172          * 
173          * @return chracter gap index
174          */
175         public int getCaretOffset() {
176                 if (!(checkCaret() && caret.isVisible()))
177                         throw new IllegalStateException(
178                                         "Caret is not active, so you can not find out its offset");
179                 return caretOffset;
180 
181         }
182 
183         /***
184          * True if caret is active and visible.
185          */
186         public boolean isCaretActive() {
187                 return checkCaret() && caret.isVisible();
188         }
189 
190         /***
191          * Activates Caret and shows it in <code>keeper</code> at character
192          * gap <code>gapIndex</code>.
193          * 
194          * @param keeper
195          *                ITextPieceKeeper where Caret will be shown
196          * @param gapIndex
197          *                character gap index, where to show caret
198          * 
199          * @see CaretManager javadoc to find out more about character gaps
200          */
201         public void activateCaret(ITextPieceKeeper keeper, int gapIndex) {
202                 if ((gapIndex < 0) || (gapIndex > keeper.getText().length()))
203                         throw new IllegalArgumentException("Gap index is wrong");
204                 caretOffset = gapIndex;
205                 keeperUnderCaret = keeper;
206                 Viewport viewport = ((FigureCanvas) canvas).getViewport();
207                 viewport.removePropertyChangeListener(propertyListener);
208                 showCaretAtCharGap(keeper, caretOffset);
209                 viewport.addPropertyChangeListener(propertyListener);
210         }
211 
212         /***
213          * Deactivates and hides Caret
214          */
215         public void deactivateCaret() {
216 
217                 if (propertyListener != null) {
218                         // try {
219                         ((FigureCanvas) canvas).getViewport()
220                                         .removePropertyChangeListener(
221                                                         propertyListener);
222                         // } catch (NullPointerException npe) {
223                         // }
224                 }
225 
226                 hideCaret();
227                 caretOffset = -1;
228                 keeperUnderCaret = null;
229         }
230 
231         /***
232          * TODO GUI ctrl movement Moves Caret from the current position to one
233          * positions further in LEFT,RIGHT,UP,DOWN or to the end or start of the
234          * current line, if possible.
235          * 
236          * 
237          * @param direction
238          *                direction of movement
239          * 
240          * @param number
241          *                of chars to move
242          * 
243          * @return true if caret was moved, false if caret wasn't moved because
244          *         there is no place where to move caret in such direction
245          */
246         public boolean moveCaret(Direction direction, int count) {
247                 if ((!checkCaret()) || (!caret.isVisible())
248                                 || (getActiveTextPieceKeeper() == null))
249                         // throw new IllegalStateException("Can not move
250                         // Caret");
251                         return false;
252 
253                 if (direction.equals(Direction.Up)
254                                 || direction.equals(Direction.Down)) {
255                         ITextPieceKeeper keeper = getActiveTextPieceKeeper();
256                         while (count > 0) {
257                                 keeper = keeper.getKeeperInDirection(direction,
258                                                 this.caret.getLocation().x);
259                                 if (keeper == null)
260                                         return false;
261                                 count--;
262                         }
263                         int caretOffset = keeper
264                                         .getTextLocator()
265                                         .getNearestCharGapIndexAtPos(
266                                                         this.caret
267                                                                         .getLocation().x);
268                         showCaretAtCharGap(keeper, caretOffset);
269                         return true;
270                 }
271                 if (direction.equals(Direction.Left)) {
272                         ITextPieceKeeper keeper = getActiveTextPieceKeeper();
273                         int offsetForMovement = this.caretOffset;
274                         while (count > offsetForMovement) {
275                                 keeper = keeper
276                                                 .getNextKeeperInDirection(direction);
277                                 if (keeper == null)
278                                         return false;
279                                 // by moving to next keeper, we make caret
280                                 // movement of one
281                                 count--;
282                                 count = count - offsetForMovement;
283                                 // last position is gap after text
284                                 offsetForMovement = keeper.getText().length();
285                         }
286 
287                         showCaretAtCharGap(keeper, offsetForMovement - count);
288                         return true;
289                 }
290                 if (direction.equals(Direction.Right)) {
291                         ITextPieceKeeper keeper = getActiveTextPieceKeeper();
292                         int offsetForMovement = keeper.getText().length()
293                                         - this.caretOffset;
294                         while (count > offsetForMovement) {
295                                 keeper = keeper
296                                                 .getNextKeeperInDirection(direction);
297                                 if (keeper == null)
298                                         return false;
299                                 // by moving to next keeper, we make caret
300                                 // movement of one
301                                 count--;
302                                 count = count - offsetForMovement;
303                                 offsetForMovement = keeper.getText().length();
304                         }
305 
306                         if (keeper == getActiveTextPieceKeeper()) {
307                                 showCaretAtCharGap(keeper, this.caretOffset
308                                                 + count);
309                                 return true;
310                         }
311                         showCaretAtCharGap(keeper, count);
312                         return true;
313                 }
314                 // not reachable
315                 throw new IllegalArgumentException();
316         }
317 
318         /***
319          * Checks if Caret is ready to use
320          * 
321          * @return <code>true</code> if caret is not <code>null</code> and
322          *         is not disposed
323          */
324         protected boolean checkCaret() {
325                 if ((this.caret == null) || this.caret.isDisposed())
326                         return false;
327                 return true;
328         }
329 
330         protected void showCaret() {
331                 if (checkCaret())
332                         if (!this.caret.isVisible())
333                                 this.caret.setVisible(true);
334         }
335 
336         protected void hideCaret() {
337                 if (checkCaret())
338                         if (this.caret.isVisible())
339                                 this.caret.setVisible(false);
340         }
341 
342         /***
343          * Shows caret at character gap charGapIndex in keeper.
344          * 
345          * @param charGapIndex
346          *                index of character gap where to show caret
347          * @param keeper
348          *                ITextPieceKeeper where to show Caret
349          */
350         protected void showCaretAtCharGap(ITextPieceKeeper keeper,
351                         int charGapIndex) {
352                 if ((charGapIndex < 0)
353                                 || (charGapIndex > keeper.getText().length()))
354                         throw new IllegalArgumentException(
355                                         "CharGapIndex is wrong.");
356                 if (checkCaret()) {
357                         Point p;
358                         if (charGapIndex == keeper.getText().length())
359                                 // index of last gap, show caret after last
360                                 // character
361                                 p = keeper.getTextLocator().getEnd(
362                                                 charGapIndex - 1);
363                         else {
364                                 p = keeper.getTextLocator().getStart(
365                                                 charGapIndex);
366                         }
367 
368                         keeper.getTextLocator().translateToAbsolute(p);
369                         this.caret.setLocation(p.x, p.y);
370                         this.caret.setSize(1, keeper.getTextLocator()
371                                         .getTextBounds().height);
372                         if (!this.caret.isVisible()) {
373                                 this.caret.setVisible(true);
374                         }
375                         this.keeperUnderCaret = keeper;
376                         this.caretOffset = charGapIndex;
377                 } else
378                         throw new IllegalStateException(
379                                         "Caret is null or disposed");
380         }
381 }