📄 keysequencetext.java
字号:
/** 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 3.2 */ 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); // Add the focus listener that attaches the global traversal filter. text.addFocusListener(new TraversalFilterManager()); // 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 3.2 */ 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. * @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 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 = endStrokeIndex - startStrokeIndex + 1; deletedKeyStrokes[0] = new KeyStroke[newLength]; final KeyStroke startStroke = keyStrokes[startStrokeIndex]; System.arraycopy(keyStrokes, 0, keyStrokes, 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); 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 3.2 */ 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 3.2 */ 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; 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()); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -