// RAP [bm]: KeyEvents ///******************************************************************************* // * Copyright (c) 2004, 2008 IBM Corporation and others. // * All rights reserved. This program and the accompanying materials // * are made available under the terms of the Eclipse Public License v1.0 // * which accompanies this distribution, and is available at // * http://www.eclipse.org/legal/epl-v10.html // * // * Contributors: // * IBM Corporation - initial API and implementation // *******************************************************************************/ // //package org.eclipse.jface.bindings.keys; // //import java.util.ArrayList; //import java.util.Collection; //import java.util.Collections; //import java.util.Iterator; //import java.util.List; //import java.util.TreeSet; // //import org.eclipse.jface.util.IPropertyChangeListener; //import org.eclipse.jface.util.PropertyChangeEvent; //import org.eclipse.swt.SWT; //import org.eclipse.swt.events.DisposeEvent; //import org.eclipse.swt.events.DisposeListener; //import org.eclipse.swt.events.FocusEvent; //import org.eclipse.swt.events.FocusListener; //import org.eclipse.swt.events.ModifyEvent; //import org.eclipse.swt.events.ModifyListener; //import org.eclipse.swt.graphics.Font; //import org.eclipse.swt.graphics.Point; //import org.eclipse.swt.widgets.Display; //import org.eclipse.swt.widgets.Event; //import org.eclipse.swt.widgets.Listener; //import org.eclipse.swt.widgets.Text; // ///** // * <p> // * A wrapper around the SWT text widget that traps literal key presses and // * converts them into key sequences for display. There are two types of key // * strokes that are displayed: complete and incomplete. A complete key stroke is // * one with a natural key, while an incomplete one has no natural key. // * Incomplete key strokes are only displayed until they are made complete or // * their component key presses are released. // * </p> // * // * @since 1.0 // */ //public final class KeySequenceText { // // /** // * A key listener that traps incoming events and displays them in the // * wrapped text field. It has no effect on traversal operations. // */ // private class KeyTrapListener implements Listener { // /** // * The index at which insertion should occur. This is used if there is a // * replacement occurring in the middle of the stroke, and the first key // * stroke was incomplete. // */ // private int insertionIndex = -1; // // /** // * Resets the insertion index to point nowhere. In other words, it is // * set to <code>-1</code>. // */ // void clearInsertionIndex() { // insertionIndex = -1; // } // // /** // * Deletes the current selection. If there is no selection, then it // * deletes the last key stroke. // * // * @param keyStrokes // * The key strokes from which to delete. This list must not // * be <code>null</code>, and must represent a valid key // * sequence. // * @return An array of keystrokes minus the keystrokes that were // * deleted. // */ // private final KeyStroke[] deleteKeyStroke(final KeyStroke[] keyStrokes) { // clearInsertionIndex(); // // if (hasSelection()) { // /* // * Delete the current selection -- disallowing incomplete // * strokes in the middle of the sequence. // */ // final KeyStroke[][] deletedKeyStrokes = new KeyStroke[1][]; // deleteSelection(keyStrokes, false, deletedKeyStrokes); // return deletedKeyStrokes[0]; // } // // // Remove the last key stroke. // if (keyStrokes.length > 0) { // final int newKeyStrokesLength = keyStrokes.length - 1; // final KeyStroke[] newKeyStrokes = new KeyStroke[newKeyStrokesLength]; // System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, // newKeyStrokesLength); // return newKeyStrokes; // } // // return keyStrokes; // } // // /** // * Handles the key pressed and released events on the wrapped text // * widget. This makes sure to either add the pressed key to the // * temporary key stroke, or complete the current temporary key stroke // * and prompt for the next. In the case of a key release, this makes // * sure that the temporary stroke is correctly displayed -- // * corresponding with modifier keys that may have been released. // * // * @param event // * The triggering event; must not be <code>null</code>. // */ // public void handleEvent(Event event) { // KeyStroke[] keyStrokes = getKeySequence().getKeyStrokes(); // // // Dispatch the event to the correct handler. // if (event.type == SWT.KeyDown) { // keyStrokes = handleKeyDown(event, keyStrokes); // } else if (event.type == SWT.KeyUp) { // keyStrokes = handleKeyUp(event, keyStrokes); // } // // // Update the underlying widget. // setKeySequence(KeySequence.getInstance(keyStrokes)); // // // Prevent the event from reaching the widget. // event.doit = false; // } // // /** // * Handles the case where the key event is an <code>SWT.KeyDown</code> // * event. This either causes a deletion (if it is an unmodified // * backspace key stroke), or an insertion (if it is any other key). // * // * @param event // * The trigger key down event; must not be <code>null</code>. // * @param keyStrokes // * The current list of key strokes. This valud must not be // * <code>null</code>, and it must represent a valid key // * sequence. // */ // private KeyStroke[] handleKeyDown(Event event, KeyStroke[] keyStrokes) { // // Is it an unmodified backspace character? // if ((event.character == SWT.BS || event.character == SWT.DEL) && (event.stateMask == 0)) { // return deleteKeyStroke(keyStrokes); // } // // return insertKeyStroke(event, keyStrokes); // } // // /** // * Handles the case where the key event is an <code>SWT.KeyUp</code> // * event. This resets the insertion index. If there is an incomplete // * stroke, then that incomplete stroke is modified to match the keys // * that are still held. If no keys are held, then the incomplete stroke // * is removed. // * // * @param event // * The triggering event; must not be <code>null</code> // * @param keyStrokes // * The key strokes that are part of the current key sequence; // * these key strokes are guaranteed to represent a valid key // * sequence. This value must not be <code>null</code>. // */ // private final KeyStroke[] handleKeyUp(final Event event, // final KeyStroke[] keyStrokes) { // if (hasIncompleteStroke()) { // /* // * Figure out the SWT integer representation of the remaining // * values. // */ // Event mockEvent = new Event(); // if ((event.keyCode & SWT.MODIFIER_MASK) != 0) { // // This key up is a modifier key being released. // mockEvent.stateMask = event.stateMask - event.keyCode; // } else { // /* // * This key up is the other end of a key down that was // * trapped by the operating system or window manager. // */ // mockEvent.stateMask = event.stateMask; // } // // /* // * Get a reasonable facsimile of the stroke that is still // * pressed. // */ // int key = SWTKeySupport // .convertEventToUnmodifiedAccelerator(mockEvent); // KeyStroke remainingStroke = SWTKeySupport // .convertAcceleratorToKeyStroke(key); // final int keyStrokesLength = keyStrokes.length; // final KeyStroke[] newKeyStrokes; // if ((keyStrokesLength > 0) // && (remainingStroke.getModifierKeys() != 0)) { // newKeyStrokes = new KeyStroke[keyStrokesLength]; // System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, // keyStrokesLength - 1); // newKeyStrokes[keyStrokesLength - 1] = remainingStroke; // // } else if (keyStrokesLength > 0) { // newKeyStrokes = new KeyStroke[keyStrokesLength - 1]; // System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, // keyStrokesLength - 1); // // } else if (remainingStroke.getModifierKeys() != 0) { // newKeyStrokes = new KeyStroke[keyStrokesLength + 1]; // System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, // keyStrokesLength); // newKeyStrokes[keyStrokesLength] = remainingStroke; // // } else { // newKeyStrokes = keyStrokes; // // } // // return newKeyStrokes; // } // // return keyStrokes; // } // // /** // * <p> // * Handles the case where a key down event is leading to a key stroke // * being inserted. The current selection is deleted, and an invalid // * remanents of the stroke are also removed. The insertion is carried // * out at the cursor position. // * </p> // * <p> // * If only a natural key is selected (as part of a larger key stroke), // * then it is possible for the user to press a natural key to replace // * the old natural key. In this situation, pressing any modifier keys // * will replace the whole thing. // * </p> // * <p> // * If the insertion point is not at the end of the sequence, then // * incomplete strokes will not be immediately inserted. Only when the // * sequence is completed is the stroke inserted. This is a requirement // * as the widget must always represent a valid key sequence. The // * insertion point is tracked using <code>insertionIndex</code>, // * which is an index into the key stroke array. // * </p> // * // * @param event // * The triggering key down event; must not be // * <code>null</code>. // * @param keyStrokes // * The key strokes into which the current stroke should be // * inserted. This value must not be <code>null</code>, and // * must represent a valid key sequence. // */ // private final KeyStroke[] insertKeyStroke(final Event event, // KeyStroke[] keyStrokes) { // // Compute the key stroke to insert. // int key = SWTKeySupport.convertEventToUnmodifiedAccelerator(event); // KeyStroke stroke = SWTKeySupport.convertAcceleratorToKeyStroke(key); // // /* // * Only insert the stroke if it is *not ScrollLock. Let's not get // * silly // */ // if ((SWT.NUM_LOCK == stroke.getNaturalKey()) // || (SWT.CAPS_LOCK == stroke.getNaturalKey()) // || (SWT.SCROLL_LOCK == stroke.getNaturalKey())) { // return keyStrokes; // } // // if (insertionIndex != -1) { // // There is a previous replacement still going on. // if (stroke.isComplete()) { // keyStrokes = insertStrokeAt(keyStrokes, stroke, // insertionIndex); // clearInsertionIndex(); // } // // } else if (hasSelection()) { // // There is a selection that needs to be replaced. // final KeyStroke[][] deletedKeyStrokes = new KeyStroke[1][]; // insertionIndex = deleteSelection(keyStrokes, stroke // .isComplete(), deletedKeyStrokes); // keyStrokes = deletedKeyStrokes[0]; // if ((stroke.isComplete()) // || (insertionIndex >= keyStrokes.length)) { // keyStrokes = insertStrokeAt(keyStrokes, stroke, // insertionIndex); // clearInsertionIndex(); // } // // } else { // // No selection, so remove the incomplete stroke, if any // if ((hasIncompleteStroke()) && (keyStrokes.length > 0)) { // final KeyStroke[] newKeyStrokes = new KeyStroke[keyStrokes.length - 1]; // System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, // keyStrokes.length - 1); // keyStrokes = newKeyStrokes; // } // // // And then add the new stroke. // if ((keyStrokes.length == 0) // || (insertionIndex >= keyStrokes.length) // || (isCursorInLastPosition())) { // keyStrokes = insertStrokeAt(keyStrokes, stroke, // keyStrokes.length); // clearInsertionIndex(); // } else { // /* // * I'm just getting the insertionIndex here. No actual // * deletion should occur. // */ // final KeyStroke[][] deletedKeyStrokes = new KeyStroke[1][]; // insertionIndex = deleteSelection(keyStrokes, stroke // .isComplete(), deletedKeyStrokes); // keyStrokes = deletedKeyStrokes[0]; // if (stroke.isComplete()) { // keyStrokes = insertStrokeAt(keyStrokes, stroke, // insertionIndex); // clearInsertionIndex(); // } // } // // } // // return keyStrokes; // } // } // // /** // * A traversal listener that blocks all traversal except for tabs and arrow // * keys. // */ // private class TraversalFilter implements Listener { // /** // * Handles the traverse event on the text field wrapped by this class. // * It swallows all traverse events example for tab and arrow key // * navigation. The other forms of navigation can be reached by tabbing // * off of the control. // * // * @param event // * The trigger event; must not be <code>null</code>. // */ // public void handleEvent(Event event) { // switch (event.detail) { // case SWT.TRAVERSE_ESCAPE: // case SWT.TRAVERSE_MNEMONIC: // case SWT.TRAVERSE_NONE: // case SWT.TRAVERSE_PAGE_NEXT: // case SWT.TRAVERSE_PAGE_PREVIOUS: // case SWT.TRAVERSE_RETURN: // event.type = SWT.None; // event.doit = false; // break; // // case SWT.TRAVERSE_TAB_NEXT: // case SWT.TRAVERSE_TAB_PREVIOUS: // // Check if modifiers other than just 'Shift' were // // down. // if ((event.stateMask & (SWT.MODIFIER_MASK ^ SWT.SHIFT)) != 0) { // // Modifiers other than shift were down. // event.type = SWT.None; // event.doit = false; // break; // } // // // fall through -- either no modifiers, or just shift. // // case SWT.TRAVERSE_ARROW_NEXT: // case SWT.TRAVERSE_ARROW_PREVIOUS: // default: // // Let the traversal happen, but clear the incomplete // // stroke // if (hasIncompleteStroke()) { // final KeyStroke[] oldKeyStrokes = getKeySequence() // .getKeyStrokes(); // final int newKeyStrokesLength = oldKeyStrokes.length - 1; // if (newKeyStrokesLength >= 1) { // final KeyStroke[] newKeyStrokes = new KeyStroke[newKeyStrokesLength]; // System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0, // newKeyStrokesLength); // setKeySequence(KeySequence.getInstance(newKeyStrokes)); // } else { // setKeySequence(KeySequence.getInstance()); // } // } // } // // } // } // // /** // * The manager resposible for installing and removing the traversal filter // * when the key sequence entry widget gains and loses focus. // */ // private class TraversalFilterManager implements FocusListener { // /** The managed filter. We only need one instance. */ // private TraversalFilter filter = new TraversalFilter(); // // private boolean filtering = false; // // /** // * Attaches the global traversal filter. // * // * @param event // * Ignored. // */ // public void focusGained(FocusEvent event) { // Display.getCurrent().addFilter(SWT.Traverse, filter); // filtering = true; // } // // /** // * Detaches the global traversal filter. // * // * @param event // * Ignored. // */ // public void focusLost(FocusEvent event) { // Display.getCurrent().removeFilter(SWT.Traverse, filter); // filtering = false; // } // // /** // * Remove the traverse filter if we close without focusOut. // */ // public void dispose() { // if (filtering) { // Display.getCurrent().removeFilter(SWT.Traverse, filter); // } // } // } // // /** // * A modification listener that makes sure that external events to this // * class (i.e., direct modification of the underlying text) do not break // * this class' view of the world. // */ // private class UpdateSequenceListener implements ModifyListener { // /** // * Handles the modify event on the underlying text widget. // * // * @param event // * The triggering event; ignored. // */ // public void modifyText(ModifyEvent event) { // try { // // The original sequence. // KeySequence originalSequence = getKeySequence(); // // // The new sequence drawn from the text. // String contents = getText(); // KeySequence newSequence = KeySequence.getInstance(contents); // // // Check to see if they're the same. // if (!originalSequence.equals(newSequence)) { // setKeySequence(newSequence); // } // // } catch (ParseException e) { // // Abort any cut/paste-driven modifications // setKeySequence(getKeySequence()); // } // } // } // // static { // TreeSet trappedKeys = new TreeSet(); // trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.TAB)); // trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.TAB // | SWT.SHIFT)); // trappedKeys.add(SWTKeySupport.convertAcceleratorToKeyStroke(SWT.BS)); // List trappedKeyList = new ArrayList(trappedKeys); // TRAPPED_KEYS = Collections.unmodifiableList(trappedKeyList); // } // // /** An empty string instance for use in clearing text values. */ // private static final String EMPTY_STRING = ""; //$NON-NLS-1$ // // /** // * The special integer value for the maximum number of strokes indicating // * that an infinite number should be allowed. // */ // public static final int INFINITE = -1; // // /** // * The name of the property representing the current key sequence in this // * key sequence widget. // * // * @since 1.0 // */ // public static final String P_KEY_SEQUENCE = "org.eclipse.jface.bindings.keys.KeySequenceText.KeySequence"; //$NON-NLS-1$ // // /** // * The keys trapped by this widget. This list is guaranteed to be roughly // * accurate. Perfection is not possible, as SWT does not export traversal // * keys as constants. // */ // public static final List TRAPPED_KEYS; // // /** // * The key filter attached to the underlying widget that traps key events. // */ // private final KeyTrapListener keyFilter = new KeyTrapListener(); // // /** // * The text of the key sequence -- containing only the complete key strokes. // */ // private KeySequence keySequence = KeySequence.getInstance(); // // /** // * Those listening to changes to the key sequence in this widget. This value // * may be <code>null</code> if there are no listeners. // */ // private Collection listeners = null; // // /** The maximum number of key strokes permitted in the sequence. */ // private int maxStrokes = INFINITE; // // /** The text widget that is wrapped for this class. */ // private final Text text; // // /** // * The listener that makes sure that the text widget remains up-to-date with // * regards to external modification of the text (e.g., cut & pasting). // */ // private final UpdateSequenceListener updateSequenceListener = new UpdateSequenceListener(); // // /** // * Constructs an instance of <code>KeySequenceTextField</code> with the // * text field to use. If the platform is carbon (MacOS X), then the font is // * set to be the same font used to display accelerators in the menus. // * // * @param wrappedText // * The text widget to wrap; must not be <code>null</code>. // */ // public KeySequenceText(Text wrappedText) { // text = wrappedText; // // // Set the font if the platform is carbon. // if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$ // // Don't worry about this font name here; it is the official menu // // font and point size on the Mac. // final Font font = new Font(text.getDisplay(), // "Lucida Grande", 13, SWT.NORMAL); //$NON-NLS-1$ // text.setFont(font); // text.addDisposeListener(new DisposeListener() { // public void widgetDisposed(DisposeEvent e) { // font.dispose(); // } // }); // } // // // Add the key listener. // text.addListener(SWT.KeyUp, keyFilter); // text.addListener(SWT.KeyDown, keyFilter); // // final TraversalFilterManager traversalFilterManager = new TraversalFilterManager(); // text.addFocusListener(traversalFilterManager); // text.addDisposeListener(new DisposeListener() { // public void widgetDisposed(DisposeEvent e) { // traversalFilterManager.dispose(); // } // }); // // // Add an internal modify listener. // text.addModifyListener(updateSequenceListener); // } // // /** // * Adds a property change listener to this key sequence widget. It will be // * notified when the key sequence changes. // * // * @param listener // * The listener to be notified when changes occur; must not be // * <code>null</code>. // * @since 1.0 // */ // public final void addPropertyChangeListener( // final IPropertyChangeListener listener) { // if (listener == null) { // return; // } // // if (listeners == null) { // listeners = new ArrayList(1); // } // // listeners.add(listener); // } // // /** // * Clears the text field and resets all the internal values. // */ // public void clear() { // final KeySequence oldKeySequence = keySequence; // keySequence = KeySequence.getInstance(); // text.setText(EMPTY_STRING); // firePropertyChangeEvent(oldKeySequence); // } // // /** // * Removes the key strokes from the list corresponding the selection. If // * <code>allowIncomplete</code>, then invalid key sequences will be // * allowed (i.e., those with incomplete strokes in the non-terminal // * position). Otherwise, incomplete strokes will be removed. This modifies // * <code>keyStrokes</code> in place, and has no effect on the text widget // * this class wraps. // * // * @param keyStrokes // * The list of key strokes from which the selection should be // * removed; must not be <code>null</code>. // * @param allowIncomplete // * Whether incomplete strokes should be allowed to exist in the // * list after the deletion. // * @param deletedKeyStrokes // * The list of keystrokes that were deleted by this operation. // * Declared as final since it will hold a reference to the new // * keyStroke array that has deleted the selected keystrokes. // * @return The index at which a subsequent insert should occur. This index // * only has meaning to the <code>insertStrokeAt</code> method. // */ // private final int deleteSelection(final KeyStroke[] keyStrokes, // final boolean allowIncomplete, final KeyStroke[][] deletedKeyStrokes) { // // Get the current selection. // Point selection = text.getSelection(); // int start = selection.x; // int end = selection.y; // // /* // * Using the key sequence format method, discover the point at which // * adding key strokes passes or equals the start of the selection. In // * other words, find the first stroke that is part of the selection. // * Keep track of the text range under which the stroke appears (i.e., // * startTextIndex->string.length() is the first selected stroke). // */ // String string = new String(); // List currentStrokes = new ArrayList(); // int startTextIndex = 0; // keeps track of the start of the stroke // final int keyStrokesLength = keyStrokes.length; // int i; // for (i = 0; (i < keyStrokesLength) && (string.length() < start); i++) { // startTextIndex = string.length(); // currentStrokes.add(keyStrokes[i]); // string = KeySequence.getInstance(currentStrokes).format(); // } // // /* // * If string.length() == start, then the cursor is positioned between // * strokes (i.e., selection is outside of a stroke). // */ // int startStrokeIndex; // if (string.length() == start) { // startStrokeIndex = currentStrokes.size(); // } else { // startStrokeIndex = currentStrokes.size() - 1; // } // // /* // * Check to see if the cursor is only positioned, rather than actually // * selecting something. We only need to compute the end if there is a // * selection. // */ // int endStrokeIndex; // if (start == end) { // // return the current keystrokes, nothing has to be deleted // deletedKeyStrokes[0] = keyStrokes; // return startStrokeIndex; // } // // for (; (i < keyStrokesLength) && (string.length() < end); i++) { // currentStrokes.add(keyStrokes[i]); // string = KeySequence.getInstance(currentStrokes).format(); // } // endStrokeIndex = currentStrokes.size() - 1; // if (endStrokeIndex < 0) { // endStrokeIndex = 0; // } // // /* // * Remove the strokes that are touched by the selection. Keep track of // * the first stroke removed. // */ // final int newLength = keyStrokesLength // - (endStrokeIndex - startStrokeIndex + 1); // deletedKeyStrokes[0] = new KeyStroke[newLength]; // final KeyStroke startStroke = keyStrokes[startStrokeIndex]; // KeyStroke keyStrokeResult[] = new KeyStroke[newLength]; // System.arraycopy(keyStrokes, 0, keyStrokeResult, 0, startStrokeIndex); // System.arraycopy(keyStrokes, endStrokeIndex + 1, keyStrokeResult, // startStrokeIndex, keyStrokesLength - endStrokeIndex - 1); // System.arraycopy(keyStrokeResult, 0, deletedKeyStrokes[0], 0, newLength); // // /* // * Allow the first stroke removed to be replaced by an incomplete // * stroke. // */ // if (allowIncomplete) { // final int modifierKeys = startStroke.getModifierKeys(); // KeyStroke incompleteStroke = KeyStroke.getInstance(modifierKeys, // KeyStroke.NO_KEY); // int incompleteStrokeLength = incompleteStroke.format().length(); // if ((startTextIndex + incompleteStrokeLength) <= start) { // final KeyStroke[] added = new KeyStroke[newLength + 1]; // System.arraycopy(deletedKeyStrokes[0], 0, added, 0, // startStrokeIndex); // added[startStrokeIndex] = incompleteStroke; // System.arraycopy(deletedKeyStrokes[0], startStrokeIndex, added, // startStrokeIndex + 1, newLength - startStrokeIndex); // deletedKeyStrokes[0] = added; // } // } // // return startStrokeIndex; // } // // /** // * Fires a property change event to all of the listeners. // * // * @param oldKeySequence // * The old key sequence; must not be <code>null</code>. // * @since 1.0 // */ // protected final void firePropertyChangeEvent( // final KeySequence oldKeySequence) { // if (listeners != null) { // final Iterator listenerItr = listeners.iterator(); // final PropertyChangeEvent event = new PropertyChangeEvent(this, // P_KEY_SEQUENCE, oldKeySequence, getKeySequence()); // while (listenerItr.hasNext()) { // final IPropertyChangeListener listener = (IPropertyChangeListener) listenerItr // .next(); // listener.propertyChange(event); // } // } // } // // /** // * An accessor for the <code>KeySequence</code> that corresponds to the // * current state of the text field. This includes incomplete strokes. // * // * @return The key sequence representation; never <code>null</code>. // */ // public KeySequence getKeySequence() { // return keySequence; // } // // /** // * An accessor for the underlying text widget's contents. // * // * @return The text contents of this entry; never <code>null</code>. // */ // private String getText() { // return text.getText(); // } // // /** // * Tests whether the current key sequence has a stroke with no natural key. // * // * @return <code>true</code> is there is an incomplete stroke; // * <code>false</code> otherwise. // */ // private boolean hasIncompleteStroke() { // return !keySequence.isComplete(); // } // // /** // * Tests whether the current text widget has some text selection. // * // * @return <code>true</code> if the number of selected characters it // * greater than zero; <code>false</code> otherwise. // */ // private boolean hasSelection() { // return (text.getSelectionCount() > 0); // } // // /** // * Inserts the key stroke at the current insertion point. This does a // * regular delete and insert, as if the key had been pressed. // * // * @param stroke // * The key stroke to insert; must not be <code>null</code>. // */ // public void insert(KeyStroke stroke) { // if (!stroke.isComplete()) { // return; // } // // // Copy the key strokes in the current key sequence. // final KeySequence keySequence = getKeySequence(); // final KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes(); // final KeyStroke[] newKeyStrokes; // if ((hasIncompleteStroke()) && (!keySequence.isEmpty())) { // final int newKeyStrokesLength = oldKeyStrokes.length - 1; // newKeyStrokes = new KeyStroke[newKeyStrokesLength]; // System.arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0, // newKeyStrokesLength); // } else { // newKeyStrokes = oldKeyStrokes; // } // // KeyStroke[][] deletedKeyStrokes = new KeyStroke[1][]; // int index = deleteSelection(newKeyStrokes, false, deletedKeyStrokes); // if (index == -1) { // index = 0; // } // // final KeyStroke[] keyStrokes = insertStrokeAt(newKeyStrokes, stroke, index); // keyFilter.clearInsertionIndex(); // setKeySequence(KeySequence.getInstance(keyStrokes)); // } // // /** // * Inserts the stroke at the given index in the list of strokes. If the // * stroke currently at that index is incomplete, then it tries to merge the // * two strokes. If merging is a complete failure (unlikely), then it will // * simply overwrite the incomplete stroke. If the stroke at the index is // * complete, then it simply inserts the stroke independently. // * // * @param keyStrokes // * The list of key strokes in which the key stroke should be // * appended; must not be <code>null</code>. // * @param stroke // * The stroke to insert; should not be <code>null</code>. // * @param index // * The index at which to insert; must be a valid index into the // * list of key strokes. // */ // private final KeyStroke[] insertStrokeAt(final KeyStroke[] keyStrokes, // KeyStroke stroke, int index) { // final int keyStrokesLength = keyStrokes.length; // final KeyStroke currentStroke = (index >= keyStrokesLength) ? null // : keyStrokes[index]; // if ((currentStroke != null) && (!currentStroke.isComplete())) { // int modifierKeys = currentStroke.getModifierKeys(); // final int naturalKey = stroke.getNaturalKey(); // modifierKeys |= stroke.getModifierKeys(); // keyStrokes[index] = KeyStroke.getInstance(modifierKeys, naturalKey); // return keyStrokes; // } // // final KeyStroke[] newKeyStrokes = new KeyStroke[keyStrokesLength + 1]; // System.arraycopy(keyStrokes, 0, newKeyStrokes, 0, index); // newKeyStrokes[index] = stroke; // if (index < keyStrokesLength) { // System.arraycopy(keyStrokes, index, newKeyStrokes, index + 1, // keyStrokesLength-index); // } // return newKeyStrokes; // } // // /** // * Tests whether the cursor is in the last position. This means that the // * selection extends to the last position. // * // * @return <code>true</code> if the selection extends to the last // * position; <code>false</code> otherwise. // */ // private boolean isCursorInLastPosition() { // return (text.getSelection().y >= getText().length()); // } // // /** // * Removes the given listener from this key sequence widget. // * // * @param listener // * The listener to be removed; must not be <code>null</code>. // * @since 1.0 // */ // public final void removePropertyChangeListener( // final IPropertyChangeListener listener) { // if ((listener == null) || (listeners == null)) { // return; // } // // listeners.remove(listener); // } // // /** // * <p> // * A mutator for the key sequence stored within this widget. The text and // * caret position are updated. // * </p> // * <p> // * All sequences are limited to maxStrokes number of strokes in length. If // * there are already that number of strokes, then it does not show // * incomplete strokes, and does not keep track of them. // * </p> // * // * @param newKeySequence // * The new key sequence for this widget; may be <code>null</code> // * if none. // */ // public void setKeySequence(KeySequence newKeySequence) { // final KeySequence oldKeySequence = keySequence; // // if (newKeySequence == null) { // text.setText(""); //$NON-NLS-1$ // } else { // keySequence = newKeySequence; // } // // // Trim any extra strokes. // if (maxStrokes != INFINITE) { // final KeyStroke[] oldKeyStrokes = keySequence.getKeyStrokes(); // if (maxStrokes < oldKeyStrokes.length) { // final KeyStroke[] newKeyStrokes = new KeyStroke[maxStrokes]; // System // .arraycopy(oldKeyStrokes, 0, newKeyStrokes, 0, // maxStrokes); // keySequence = KeySequence.getInstance(newKeyStrokes); // } // } // // // Check to see if the text has changed. // String currentString = getText(); // String newString = keySequence.format(); // if (!currentString.equals(newString)) { // // We need to update the text // text.removeModifyListener(updateSequenceListener); // text.setText(keySequence.format()); // text.addModifyListener(updateSequenceListener); // text.setSelection(getText().length()); // } // // firePropertyChangeEvent(oldKeySequence); // } // // /** // * Returns the maximum number of strokes that are permitted in this widget // * at one time. // * // * @return The maximum number of strokes; will be a positive integer or // * <code>INFINITE</code>. // */ // public int getKeyStrokeLimit() { // return maxStrokes; // } // // /** // * A mutator for the maximum number of strokes that are permitted in this // * widget at one time. // * // * @param keyStrokeLimit // * The maximum number of strokes; must be a positive integer or // * <code>INFINITE</code>. // */ // public void setKeyStrokeLimit(int keyStrokeLimit) { // if (keyStrokeLimit > 0 || keyStrokeLimit == INFINITE) { // this.maxStrokes = keyStrokeLimit; // } else { // throw new IllegalArgumentException(); // } // // // Make sure we are obeying the new limit. // setKeySequence(getKeySequence()); // } //}