📄 keysequencetext.java
字号:
/******************************************************************************* * Copyright (c) 2004, 2006 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 3.1 */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. */ 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.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(); /** * Attaches the global traversal filter. * * @param event * Ignored. */ public void focusGained(FocusEvent event) { Display.getCurrent().addFilter(SWT.Traverse, filter); } /** * Detaches the global traversal filter. * * @param event * Ignored. */ public void focusLost(FocusEvent event) { 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -