KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > keys > KeySequenceText


1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.ui.internal.keys;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.SortedSet JavaDoc;
19 import java.util.TreeSet JavaDoc;
20
21 import org.eclipse.swt.SWT;
22 import org.eclipse.swt.events.DisposeEvent;
23 import org.eclipse.swt.events.DisposeListener;
24 import org.eclipse.swt.events.FocusEvent;
25 import org.eclipse.swt.events.FocusListener;
26 import org.eclipse.swt.events.ModifyEvent;
27 import org.eclipse.swt.events.ModifyListener;
28 import org.eclipse.swt.graphics.Font;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.widgets.Display;
31 import org.eclipse.swt.widgets.Event;
32 import org.eclipse.swt.widgets.Listener;
33 import org.eclipse.swt.widgets.Text;
34 import org.eclipse.ui.keys.KeySequence;
35 import org.eclipse.ui.keys.KeyStroke;
36 import org.eclipse.ui.keys.NaturalKey;
37 import org.eclipse.ui.keys.ParseException;
38 import org.eclipse.ui.keys.SWTKeySupport;
39 import org.eclipse.ui.keys.SpecialKey;
40
41 /**
42  * A wrapper around the SWT text widget that traps literal key presses and
43  * converts them into key sequences for display. There are two types of key
44  * strokes that are displayed: complete and incomplete. A complete key stroke
45  * is one with a natural key, while an incomplete one has no natural key.
46  * Incomplete key strokes are only displayed until they are made complete or
47  * their component key presses are released.
48  */

49 public final class KeySequenceText {
50
51     /**
52      * A key listener that traps incoming events and displays them in the
53      * wrapped text field. It has no effect on traversal operations.
54      */

55     private class KeyTrapListener implements Listener {
56         /**
57          * The index at which insertion should occur. This is used if there is
58          * a replacement occurring in the middle of the stroke, and the first
59          * key stroke was incomplete.
60          */

61         private int insertionIndex = -1;
62
63         /**
64          * Resets the insertion index to point nowhere. In other words, it is
65          * set to <code>-1</code>.
66          */

67         void clearInsertionIndex() {
68             insertionIndex = -1;
69         }
70
71         /**
72          * Deletes the current selection. If there is no selection, then it
73          * deletes the last key stroke.
74          *
75          * @param keyStrokes
76          * The key strokes from which to delete. This list must not
77          * be <code>null</code>, and must represent a valid key
78          * sequence.
79          */

80         private void deleteKeyStroke(List JavaDoc keyStrokes) {
81             clearInsertionIndex();
82
83             if (hasSelection()) {
84                 /*
85                  * Delete the current selection -- disallowing incomplete
86                  * strokes in the middle of the sequence.
87                  */

88                 deleteSelection(keyStrokes, false);
89
90             } else {
91                 // Remove the last key stroke.
92
if (!keyStrokes.isEmpty()) {
93                     keyStrokes.remove(keyStrokes.size() - 1);
94                 }
95
96             }
97         }
98
99         /**
100          * Handles the key pressed and released events on the wrapped text
101          * widget. This makes sure to either add the pressed key to the
102          * temporary key stroke, or complete the current temporary key stroke
103          * and prompt for the next. In the case of a key release, this makes
104          * sure that the temporary stroke is correctly displayed --
105          * corresponding with modifier keys that may have been released.
106          *
107          * @param event
108          * The triggering event; must not be <code>null</code>.
109          */

110         public void handleEvent(Event event) {
111             List JavaDoc keyStrokes = new ArrayList JavaDoc(getKeySequence().getKeyStrokes());
112
113             // Dispatch the event to the correct handler.
114
if (event.type == SWT.KeyDown) {
115                 handleKeyDown(event, keyStrokes);
116             } else if (event.type == SWT.KeyUp) {
117                 handleKeyUp(event, keyStrokes);
118             }
119
120             // Update the underlying widget.
121
setKeySequence(KeySequence.getInstance(keyStrokes));
122
123             // Prevent the event from reaching the widget.
124
event.doit = false;
125         }
126
127         /**
128          * Handles the case where the key event is an <code>SWT.KeyDown</code>
129          * event. This either causes a deletion (if it is an unmodified
130          * backspace key stroke), or an insertion (if it is any other key).
131          *
132          * @param event
133          * The trigger key down event; must not be <code>null</code>.
134          * @param keyStrokes
135          * The current list of key strokes. This valud must not be
136          * <code>null</code>, and it must represent a valid key
137          * sequence.
138          */

139         private void handleKeyDown(Event event, List JavaDoc keyStrokes) {
140             // Is it an unmodified backspace character?
141
if ((event.character == SWT.BS) && (event.stateMask == 0)) {
142                 deleteKeyStroke(keyStrokes);
143             } else {
144                 insertKeyStroke(event, keyStrokes);
145             }
146         }
147
148         /**
149          * Handles the case where the key event is an <code>SWT.KeyUp</code>
150          * event. This resets the insertion index. If there is an incomplete
151          * stroke, then that incomplete stroke is modified to match the keys
152          * that are still held. If no keys are held, then the incomplete stroke
153          * is removed.
154          *
155          * @param event
156          * The triggering event; must not be <code>null</code>
157          * @param keyStrokes
158          * The key strokes that are part of the current key
159          * sequence; these key strokes are guaranteed to represent a
160          * valid key sequence. This valud must not be <code>null</code>.
161          */

162         private void handleKeyUp(Event event, List JavaDoc keyStrokes) {
163             if (hasIncompleteStroke()) {
164                 /*
165                  * Figure out the SWT integer representation of the remaining
166                  * values.
167                  */

168                 Event mockEvent = new Event();
169                 if ((event.keyCode & SWT.MODIFIER_MASK) != 0) {
170                     // This key up is a modifier key being released.
171
mockEvent.stateMask = event.stateMask - event.keyCode;
172                 } else {
173                     /*
174                      * This key up is the other end of a key down that was
175                      * trapped by the operating system or window manager.
176                      */

177                     mockEvent.stateMask = event.stateMask;
178                 }
179
180                 /*
181                  * Get a reasonable facsimile of the stroke that is still
182                  * pressed.
183                  */

184                 int key =
185                     SWTKeySupport.convertEventToUnmodifiedAccelerator(mockEvent);
186                 KeyStroke remainingStroke =
187                     SWTKeySupport.convertAcceleratorToKeyStroke(key);
188                 if (!keyStrokes.isEmpty()) {
189                     keyStrokes.remove(keyStrokes.size() - 1);
190                 }
191                 if (!remainingStroke.getModifierKeys().isEmpty()) {
192                     keyStrokes.add(remainingStroke);
193                 }
194             }
195
196         }
197
198         /**
199          * <p>
200          * Handles the case where a key down event is leading to a key stroke
201          * being inserted. The current selection is deleted, and an invalid
202          * remanents of the stroke are also removed. The insertion is carried
203          * out at the cursor position.
204          * </p>
205          * <p>
206          * If only a natural key is selected (as part of a larger key stroke),
207          * then it is possible for the user to press a natural key to replace
208          * the old natural key. In this situation, pressing any modifier keys
209          * will replace the whole thing.
210          * </p>
211          * <p>
212          * If the insertion point is not at the end of the sequence, then
213          * incomplete strokes will not be immediately inserted. Only when the
214          * sequence is completed is the stroke inserted. This is a requirement
215          * as the widget must always represent a valid key sequence. The
216          * insertion point is tracked using <code>insertionIndex</code>,
217          * which is an index into the key stroke array.
218          * </p>
219          *
220          * @param event
221          * The triggering key down event; must not be <code>null</code>.
222          * @param keyStrokes
223          * The key strokes into which the current stroke should be
224          * inserted. This value must not be <code>null</code>,
225          * and must represent a valid key sequence.
226          */

227         private void insertKeyStroke(Event event, List JavaDoc keyStrokes) {
228             // Compute the key stroke to insert.
229
int key = SWTKeySupport.convertEventToUnmodifiedAccelerator(event);
230             KeyStroke stroke = SWTKeySupport.convertAcceleratorToKeyStroke(key);
231
232             /*
233              * Only insert the stroke if it is *not ScrollLock. Let's not get
234              * silly
235              */

236             if ((SpecialKey.NUM_LOCK.equals(stroke.getNaturalKey()))
237                 || (SpecialKey.CAPS_LOCK.equals(stroke.getNaturalKey()))
238                 || (SpecialKey.SCROLL_LOCK.equals(stroke.getNaturalKey()))) {
239                 return;
240             }
241
242             if (insertionIndex != -1) {
243                 // There is a previous replacement still going on.
244
if (stroke.isComplete()) {
245                     insertStrokeAt(keyStrokes, stroke, insertionIndex);
246                     clearInsertionIndex();
247                 }
248
249             } else if (hasSelection()) {
250                 // There is a selection that needs to be replaced.
251
insertionIndex =
252                     deleteSelection(keyStrokes, stroke.isComplete());
253                 if ((stroke.isComplete())
254                     || (insertionIndex >= keyStrokes.size())) {
255                     insertStrokeAt(keyStrokes, stroke, insertionIndex);
256                     clearInsertionIndex();
257                 }
258
259             } else {
260                 // No selection, so remove the incomplete stroke, if any
261
if ((hasIncompleteStroke()) && (!keyStrokes.isEmpty())) {
262                     keyStrokes.remove(keyStrokes.size() - 1);
263                 }
264
265                 // And then add the new stroke.
266
if ((keyStrokes.isEmpty())
267                     || (insertionIndex >= keyStrokes.size())
268                     || (isCursorInLastPosition())) {
269                     insertStrokeAt(keyStrokes, stroke, keyStrokes.size());
270                     clearInsertionIndex();
271                 } else {
272                     /*
273                      * I'm just getting the insertionIndex here. No actual
274                      * deletion should occur.
275                      */

276                     insertionIndex =
277                         deleteSelection(keyStrokes, stroke.isComplete());
278                     if (stroke.isComplete()) {
279                         insertStrokeAt(keyStrokes, stroke, insertionIndex);
280                         clearInsertionIndex();
281                     }
282                 }
283
284             }
285         }
286     }
287
288     /**
289      * A traversal listener that blocks all traversal except for tabs and arrow
290      * keys.
291      */

292     private class TraversalFilter implements Listener {
293         /**
294          * Handles the traverse event on the text field wrapped by this class.
295          * It swallows all traverse events example for tab and arrow key
296          * navigation. The other forms of navigation can be reached by tabbing
297          * off of the control.
298          *
299          * @param event
300          * The trigger event; must not be <code>null</code>.
301          */

302         public void handleEvent(Event event) {
303             switch (event.detail) {
304                 case SWT.TRAVERSE_ESCAPE :
305                 case SWT.TRAVERSE_MNEMONIC :
306                 case SWT.TRAVERSE_NONE :
307                 case SWT.TRAVERSE_PAGE_NEXT :
308                 case SWT.TRAVERSE_PAGE_PREVIOUS :
309                 case SWT.TRAVERSE_RETURN :
310                     event.type = SWT.None;
311                     event.doit = false;
312                     break;
313
314                 case SWT.TRAVERSE_TAB_NEXT :
315                 case SWT.TRAVERSE_TAB_PREVIOUS :
316                     // Check if modifiers other than just 'Shift' were
317
// down.
318
if ((event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT))
319                         != 0) {
320                         // Modifiers other than shift were down.
321
event.type = SWT.None;
322                         event.doit = false;
323                         break;
324                     }
325                     
326                     // fall through -- either no modifiers, or just shift.
327

328                 case SWT.TRAVERSE_ARROW_NEXT :
329                 case SWT.TRAVERSE_ARROW_PREVIOUS :
330                 default :
331                     // Let the traversal happen, but clear the incomplete
332
// stroke
333
if (hasIncompleteStroke()) {
334                         List JavaDoc keyStrokes =
335                             new ArrayList JavaDoc(getKeySequence().getKeyStrokes());
336                         if (!keyStrokes.isEmpty()) {
337                             keyStrokes.remove(keyStrokes.size() - 1);
338                         }
339                         setKeySequence(KeySequence.getInstance(keyStrokes));
340                     }
341             }
342
343         }
344     }
345
346     /**
347      * The manager resposible for installing and removing the traversal filter
348      * when the key sequence entry widget gains and loses focus.
349      */

350     private class TraversalFilterManager implements FocusListener {
351         /** The managed filter. We only need one instance. */
352         private TraversalFilter filter = new TraversalFilter();
353
354         /**
355          * Attaches the global traversal filter.
356          *
357          * @param event
358          * Ignored.
359          */

360         public void focusGained(FocusEvent event) {
361             Display.getCurrent().addFilter(SWT.Traverse, filter);
362         }
363
364         /**
365          * Detaches the global traversal filter.
366          *
367          * @param event
368          * Ignored.
369          */

370         public void focusLost(FocusEvent event) {
371             Display.getCurrent().removeFilter(SWT.Traverse, filter);
372         }
373     }
374
375     /**
376      * A modification listener that makes sure that external events to this
377      * class (i.e., direct modification of the underlying text) do not break
378      * this class' view of the world.
379      */

380     private class UpdateSequenceListener implements ModifyListener {
381         /**
382          * Handles the modify event on the underlying text widget.
383          *
384          * @param event
385          * The triggering event; ignored.
386          */

387         public void modifyText(ModifyEvent event) {
388             try {
389                 // The original sequence.
390
KeySequence originalSequence = getKeySequence();
391
392                 // The new sequence drawn from the text.
393
String JavaDoc contents = getText();
394                 KeySequence newSequence = KeySequence.getInstance(contents);
395
396                 // Check to see if they're the same.
397
if (!originalSequence.equals(newSequence)) {
398                     setKeySequence(newSequence);
399                 }
400
401             } catch (ParseException e) {
402                 // Abort any cut/paste-driven modifications
403
setKeySequence(getKeySequence());
404             }
405         }
406     }
407
408     static {
409         TreeSet JavaDoc trappedKeys = new TreeSet JavaDoc();
410         trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.TAB));
411         trappedKeys.add(
412             SWTKeySupport.convertAcceleratorToKeyStroke(SWT.TAB | SWT.SHIFT));
413         trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.BS));
414         List JavaDoc trappedKeyList = new ArrayList JavaDoc(trappedKeys);
415         TRAPPED_KEYS = Collections.unmodifiableList(trappedKeyList);
416     }
417
418     /** An empty string instance for use in clearing text values. */
419     private static final String JavaDoc EMPTY_STRING = ""; //$NON-NLS-1$
420
/**
421      * The special integer value for the maximum number of strokes indicating
422      * that an infinite number should be allowed.
423      */

424     public static final int INFINITE = -1;
425     /**
426      * The keys trapped by this widget. This list is guaranteed to be roughly
427      * accurate. Perfection is not possible, as SWT does not export traversal
428      * keys as constants.
429      */

430     public static final List JavaDoc TRAPPED_KEYS;
431
432     /**
433      * The key filter attached to the underlying widget that traps key events.
434      */

435     private final KeyTrapListener keyFilter = new KeyTrapListener();
436     /**
437      * The text of the key sequence -- containing only the complete key
438      * strokes.
439      */

440     private KeySequence keySequence = KeySequence.getInstance();
441     /** The maximum number of key strokes permitted in the sequence. */
442     private int maxStrokes = INFINITE;
443     /** The text widget that is wrapped for this class. */
444     private final Text text;
445     /**
446      * The listener that makes sure that the text widget remains up-to-date
447      * with regards to external modification of the text (e.g., cut & pasting).
448      */

449     private final UpdateSequenceListener updateSequenceListener =
450         new UpdateSequenceListener();
451
452     /**
453      * Constructs an instance of <code>KeySequenceTextField</code> with the
454      * text field to use. If the platform is carbon (MacOS X), then the font is
455      * set to be the same font used to display accelerators in the menus.
456      *
457      * @param wrappedText
458      * The text widget to wrap; must not be <code>null</code>.
459      */

460     public KeySequenceText(Text wrappedText) {
461         text = wrappedText;
462
463         // Set the font if the platform is carbon.
464
if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$
465
// Don't worry about this font name here; it is the official menu
466
// font and point size on the Mac.
467
final Font font = new Font(text.getDisplay(), "Lucida Grande", 13, SWT.NORMAL); //$NON-NLS-1$
468
text.setFont(font);
469             text.addDisposeListener(new DisposeListener() {
470                 public void widgetDisposed(DisposeEvent e) {
471                     font.dispose();
472                 }
473             });
474         }
475
476         // Add the key listener.
477
text.addListener(SWT.KeyUp, keyFilter);
478         text.addListener(SWT.KeyDown, keyFilter);
479
480         // Add the focus listener that attaches the global traversal filter.
481
text.addFocusListener(new TraversalFilterManager());
482
483         // Add an internal modify listener.
484
text.addModifyListener(updateSequenceListener);
485     }
486
487     /**
488      * Clears the text field and resets all the internal values.
489      */

490     public void clear() {
491         keySequence = KeySequence.getInstance();
492         text.setText(EMPTY_STRING);
493     }
494
495     /**
496      * Removes the key strokes from the list corresponding the selection. If
497      * <code>allowIncomplete</code>, then invalid key sequences will be
498      * allowed (i.e., those with incomplete strokes in the non-terminal
499      * position). Otherwise, incomplete strokes will be removed. This modifies
500      * <code>keyStrokes</code> in place, and has no effect on the text widget
501      * this class wraps.
502      *
503      * @param keyStrokes
504      * The list of key strokes from which the selection should be
505      * removed; must not be <code>null</code>.
506      * @param allowIncomplete
507      * Whether incomplete strokes should be allowed to exist in the
508      * list after the deletion.
509      * @return The index at which a subsequent insert should occur. This index
510      * only has meaning to the <code>insertStrokeAt</code> method.
511      */

512     private int deleteSelection(List JavaDoc keyStrokes, boolean allowIncomplete) {
513         // Get the current selection.
514
Point selection = text.getSelection();
515         int start = selection.x;
516         int end = selection.y;
517
518         /*
519          * Using the key sequence format method, discover the point at which
520          * adding key strokes passes or equals the start of the selection. In
521          * other words, find the first stroke that is part of the selection.
522          * Keep track of the text range under which the stroke appears (i.e.,
523          * startTextIndex->string.length() is the first selected stroke).
524          */

525         String JavaDoc string = new String JavaDoc();
526         List JavaDoc currentStrokes = new ArrayList JavaDoc();
527         Iterator JavaDoc keyStrokeItr = keyStrokes.iterator();
528         int startTextIndex = 0; // keeps track of the start of the stroke
529
while ((string.length() < start) && (keyStrokeItr.hasNext())) {
530             startTextIndex = string.length();
531             currentStrokes.add(keyStrokeItr.next());
532             string = KeySequence.getInstance(currentStrokes).format();
533         }
534
535         /*
536          * If string.length() == start, then the cursor is positioned between
537          * strokes (i.e., selection is outside of a stroke).
538          */

539         int startStrokeIndex;
540         if (string.length() == start) {
541             startStrokeIndex = currentStrokes.size();
542         } else {
543             startStrokeIndex = currentStrokes.size() - 1;
544         }
545
546         /*
547          * Check to see if the cursor is only positioned, rather than actually
548          * selecting something. We only need to compute the end if there is a
549          * selection.
550          */

551         int endStrokeIndex;
552         if (start == end) {
553             return startStrokeIndex;
554
555         } else {
556             while ((string.length() < end) && (keyStrokeItr.hasNext())) {
557                 currentStrokes.add(keyStrokeItr.next());
558                 string = KeySequence.getInstance(currentStrokes).format();
559             }
560             endStrokeIndex = currentStrokes.size() - 1;
561             if (endStrokeIndex < 0) {
562                 endStrokeIndex = 0;
563             }
564
565         }
566
567         /*
568          * Remove the strokes that are touched by the selection. Keep track of
569          * the first stroke removed.
570          */

571         KeyStroke startStroke = (KeyStroke) keyStrokes.get(startStrokeIndex);
572         while (startStrokeIndex <= endStrokeIndex) {
573             keyStrokes.remove(startStrokeIndex);
574             endStrokeIndex--;
575         }
576
577         /*
578          * Allow the first stroke removed to be replaced by an incomplete
579          * stroke.
580          */

581         if (allowIncomplete) {
582             SortedSet JavaDoc modifierKeys = new TreeSet JavaDoc(startStroke.getModifierKeys());
583             KeyStroke incompleteStroke =
584                 KeyStroke.getInstance(modifierKeys, null);
585             int incompleteStrokeLength = incompleteStroke.format().length();
586             if ((startTextIndex + incompleteStrokeLength) <= start) {
587                 keyStrokes.add(startStrokeIndex, incompleteStroke);
588             }
589         }
590
591         return startStrokeIndex;
592     }
593
594     /**
595      * An accessor for the <code>KeySequence</code> that corresponds to the
596      * current state of the text field. This includes incomplete strokes.
597      *
598      * @return The key sequence representation; never <code>null</code>.
599      */

600     public KeySequence getKeySequence() {
601         return keySequence;
602     }
603
604     /**
605      * An accessor for the underlying text widget's contents.
606      *
607      * @return The text contents of this entry; never <code>null</code>.
608      */

609     private String JavaDoc getText() {
610         return text.getText();
611     }
612
613     /**
614      * Tests whether the current key sequence has a stroke with no natural key.
615      *
616      * @return <code>true</code> is there is an incomplete stroke; <code>false</code>
617      * otherwise.
618      */

619     private boolean hasIncompleteStroke() {
620         return !keySequence.isComplete();
621     }
622
623     /**
624      * Tests whether the current text widget has some text selection.
625      *
626      * @param <code>true</code> if the number of selected characters it greater
627      * than zero; <code>false</code> otherwise.
628      */

629     private boolean hasSelection() {
630         return (text.getSelectionCount() > 0);
631     }
632
633     /**
634      * Inserts the key stroke at the current insertion point. This does a
635      * regular delete and insert, as if the key had been pressed.
636      *
637      * @param stroke
638      * The key stroke to insert; must not be <code>null</code>.
639      */

640     public void insert(KeyStroke stroke) {
641         if (!stroke.isComplete()) {
642             return;
643         }
644
645         // Copy the key strokes in the current key sequence.
646
List JavaDoc keyStrokes = new ArrayList JavaDoc(getKeySequence().getKeyStrokes());
647
648         if ((hasIncompleteStroke()) && (!keyStrokes.isEmpty())) {
649             keyStrokes.remove(keyStrokes.size() - 1);
650         }
651
652         int index = deleteSelection(keyStrokes, false);
653         insertStrokeAt(keyStrokes, stroke, index);
654         keyFilter.clearInsertionIndex();
655         setKeySequence(KeySequence.getInstance(keyStrokes));
656     }
657
658     /**
659      * Inserts the stroke at the given index in the list of strokes. If the
660      * stroke currently at that index is incomplete, then it tries to merge the
661      * two strokes. If merging is a complete failure (unlikely), then it will
662      * simply overwrite the incomplete stroke. If the stroke at the index is
663      * complete, then it simply inserts the stroke independently.
664      *
665      * @param keyStrokes
666      * The list of key strokes in which the key stroke should be
667      * appended; must not be <code>null</code>.
668      * @param stroke
669      * The stroke to insert; should not be <code>null</code>.
670      * @param index
671      * The index at which to insert; must be a valid index into the
672      * list of key strokes.
673      */

674     private void insertStrokeAt(List JavaDoc keyStrokes, KeyStroke stroke, int index) {
675         KeyStroke currentStroke =
676             (index >= keyStrokes.size())
677                 ? null
678                 : (KeyStroke) keyStrokes.get(index);
679         if ((currentStroke != null) && (!currentStroke.isComplete())) {
680             SortedSet JavaDoc modifierKeys =
681                 new TreeSet JavaDoc(currentStroke.getModifierKeys());
682             NaturalKey naturalKey = stroke.getNaturalKey();
683             modifierKeys.addAll(stroke.getModifierKeys());
684             keyStrokes.remove(index);
685             keyStrokes.add(
686                 index,
687                 KeyStroke.getInstance(modifierKeys, naturalKey));
688         } else {
689             keyStrokes.add(index, stroke);
690         }
691     }
692
693     /**
694      * Tests whether the cursor is in the last position. This means that the
695      * selection extends to the last position.
696      *
697      * @return <code>true</code> if the selection extends to the last
698      * position; <code>false</code> otherwise.
699      */

700     private boolean isCursorInLastPosition() {
701         return (text.getSelection().y >= getText().length());
702     }
703
704     /**
705      * <p>
706      * A mutator for the key sequence stored within this widget. The text and
707      * caret position are updated.
708      * </p>
709      * <p>
710      * All sequences are limited to maxStrokes number of strokes in length. If
711      * there are already that number of strokes, then it does not show
712      * incomplete strokes, and does not keep track of them.
713      * </p>
714      *
715      * @param newKeySequence
716      * The new key sequence for this widget; may be <code>null</code>
717      * if none.
718      */

719     public void setKeySequence(KeySequence newKeySequence) {
720         keySequence = newKeySequence;
721
722         // Trim any extra strokes.
723
if (maxStrokes != INFINITE) {
724             List JavaDoc keyStrokes = new ArrayList JavaDoc(keySequence.getKeyStrokes());
725             int keyStrokesSize = keyStrokes.size();
726             for (int i = keyStrokesSize - 1; i >= maxStrokes; i--) {
727                 keyStrokes.remove(i);
728             }
729             keySequence = KeySequence.getInstance(keyStrokes);
730         }
731
732         // Check to see if the text has changed.
733
String JavaDoc currentString = getText();
734         String JavaDoc newString = keySequence.format();
735         if (!currentString.equals(newString)) {
736             // We need to update the text
737
text.removeModifyListener(updateSequenceListener);
738             text.setText(keySequence.format());
739             text.addModifyListener(updateSequenceListener);
740             text.setSelection(getText().length());
741         }
742     }
743
744     /**
745      * Returns the maximum number of strokes that are permitted in this widget
746      * at one time.
747      *
748      * @return The maximum number of strokes; will be a positive integer or
749      * <code>INFINITE</code>.
750      */

751     public int getKeyStrokeLimit() {
752         return maxStrokes;
753     }
754
755     /**
756      * A mutator for the maximum number of strokes that are permitted in this
757      * widget at one time.
758      *
759      * @param keyStrokeLimit
760      * The maximum number of strokes; must be a positive integer or
761      * <code>INFINITE</code>.
762      */

763     public void setKeyStrokeLimit(int keyStrokeLimit) {
764         if (keyStrokeLimit > 0 || keyStrokeLimit == INFINITE)
765             this.maxStrokes = keyStrokeLimit;
766         else
767             throw new IllegalArgumentException JavaDoc();
768
769         // Make sure we are obeying the new limit.
770
setKeySequence(getKeySequence());
771     }
772 }
773
Popular Tags