/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.wave.client.common.util; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.user.client.Event; /** * Attempts to bring sanity to the incredibly complex and inconsistent world of * browser events, especially with regards to key events. * * {@link SignalEventImpl} * * @author danilatos@google.com (Daniel Danilatos) */ public interface SignalEvent { /** * The "type" of key signal. The type is defined by us, based on a convenient * classification of these events. */ public enum KeySignalType { /** * Typing in the form of inputting text * * NOTE: "TAB" is treated as INPUT. */ INPUT, /** Moving around */ NAVIGATION, /** Deleting text (Backspace + Delete) */ DELETE, /** Other, like ESC or F3, that by themselves will do nothing to the document */ NOEFFECT, /** Sentinal for debugging purposes only */ SENTINAL } /** * The "movement unit" of this event. For example, on windows and linux, * holding down CTRL whilst navigating left and right with the arrow * keys moves a word at a time. Even more importantly, move units * are used for deleting text. */ public enum MoveUnit { /** A character at a time */ CHARACTER, /** A word at a time */ WORD, /** To the start or end of the line */ LINE, /** A "page" at a time */ PAGE, /** To the start or end of the document */ DOCUMENT } /** * These are the currently supported modifier combinations * * Supporting more requires tricky, browser-specific code. Go to the experiment harness * and have a look at the wonders.... */ public enum KeyModifier { /** No modifiers are pressed */ NONE { @Override public boolean check(SignalEvent event) { return !event.getShiftKey() && !event.getAltKey() && !event.getCtrlKey() && !event.getMetaKey(); } }, /** Only shift is pressed */ SHIFT { @Override public boolean check(SignalEvent event) { return event.getShiftKey() && !event.getAltKey() && !event.getCtrlKey() && !event.getMetaKey(); } }, /** Only ctrl is pressed */ CTRL { @Override public boolean check(SignalEvent event) { return !event.getShiftKey() && !event.getAltKey() && event.getCtrlKey() && !event.getMetaKey(); } }, /** Only alt is pressed */ ALT { @Override public boolean check(SignalEvent event) { return !event.getShiftKey() && event.getAltKey() && !event.getCtrlKey() && !event.getMetaKey(); } }, /** Only the meta key is pressed */ META { @Override public boolean check(SignalEvent event) { return !event.getShiftKey() && !event.getAltKey() && !event.getCtrlKey() && event.getMetaKey(); } }, /** * Only the "command" key is pressed * @see SignalEvent#COMMAND_IS_CTRL */ COMMAND { @Override public boolean check(SignalEvent event) { return COMMAND_IS_CTRL ? CTRL.check(event) : META.check(event); } }, /** Both ctrl and alt, but no others, are pressed */ CTRL_ALT { @Override public boolean check(SignalEvent event) { return !event.getShiftKey() && event.getAltKey() && event.getCtrlKey() && !event.getMetaKey(); } }, /** Both "command" and shift, but no others, are pressed */ COMMAND_SHIFT { @Override public boolean check(SignalEvent event) { // The ctrl/meta key which is NOT the command key boolean notCommandKey = COMMAND_IS_CTRL ? event.getMetaKey() : event.getCtrlKey(); return event.getShiftKey() && !event.getAltKey() && event.getCommandKey() && !notCommandKey; } } ; /** * Whether the "command" key is the control key (as opposed to the meta key). */ // TODO(danilatos): Reconcile this with the value in SignalKeyLogic. private static final boolean COMMAND_IS_CTRL = !UserAgent.isMac(); /** * Check if the given event has the enum value's modifiers pressed. * @param event * @return true if they are pressed */ public abstract boolean check(SignalEvent event); } /** * @return Event type as a string, e.g. "keypress" */ String getType(); /** * @return The target element of the event */ Element getTarget(); /** * @return true if the event is a key event * TODO(danilatos): Have a top level EventSignalType enum */ boolean isKeyEvent(); /** * @return true if it is an IME composition event */ boolean isCompositionEvent(); /** * Returns true if the key event is an IME input event. * Only makes sense to call this method if this is a key signal. * Does not work on FF. (TODO(danilatos): Can it be done? Tricks * with dom mutation events?) * * @return true if this is an IME input event */ boolean isImeKeyEvent(); /** * @return true if this is a mouse event * TODO(danilatos): Have a top level EventSignalType enum */ boolean isMouseEvent(); /** * TODO(danilatos): Click + drag? I.e. return true for mouse move, if the * button is pressed? (this might be useful for tracking changing selections * as the user holds & drags) * @return true if this is an event involving some use of mouse buttons */ boolean isMouseButtonEvent(); /** * @return true if this is a mouse event but not {@link #isMouseButtonEvent()} */ boolean isMouseButtonlessEvent(); /** * @return true if this is a "click" event */ boolean isClickEvent(); /** * @return True if this is a dom mutation event */ boolean isMutationEvent(); /** * @return true if this is any sort of clipboard event */ boolean isClipboardEvent(); /** * @return If this is a focus event */ boolean isFocusEvent(); /** * @return true if this is a paste event * TODO(danilatos): Make a ClipboardSignalType enum instead */ boolean isPasteEvent(); /** * @return true if this is a cut event * TODO(danilatos): Make a ClipboardSignalType enum instead */ boolean isCutEvent(); /** * @return true if this is a copy event * TODO(danilatos): Make a ClipboardSignalType enum instead */ boolean isCopyEvent(); /** * @return true if the command key is depressed * @see #COMMAND_IS_CTRL */ boolean getCommandKey(); /** * @return true if the ctrl key is depressed */ boolean getCtrlKey(); /** * @return true if the meta key is depressed */ boolean getMetaKey(); /** * @return true if the alt key is depressed */ boolean getAltKey(); /** * @return true if the shift key is depressed */ boolean getShiftKey(); /** * TODO(user): Deprecate this, as it breaks abstraction. Also prevent people * from casting back and forth. * * @return The underlying event view of this event */ Event asEvent(); /** * Only valid for key events. * Currently only implemented for deleting, not actual navigating. * @return The move unit of this event */ MoveUnit getMoveUnit(); /** * @return True if the event is the key combo for "undo" * NOTE(danilatos): This is the best we have at detecting undo events :( * We need even more special handling for undo done via the menu :( :( */ boolean isUndoCombo(); /** * @return True if the event is the key combo for "redo" */ boolean isRedoCombo(); /** * @param letter Treated case-insensitive, including things like '1' vs '!' * User may provide either, but upper case for letters and unshifted for * other keys is recommended * @param modifier * @return True if the given letter is pressed, and only the given modifiers. */ boolean isCombo(int letter, KeyModifier modifier); /** * @param letter * @return true, if the given letter was pressed without modifiers. Takes into * account the caps lock key being pressed (it will be as if it * weren't pressed) */ boolean isOnly(int letter); /** * @return The gwtKeyCode of this event, with some minor compatibility * adjustments */ int getKeyCode(); /** * @return the mouse button bit field for this event. The masks used to extract * individual buttons from the field are {@link NativeEvent#BUTTON_LEFT}, * {@link NativeEvent#BUTTON_MIDDLE}, and {@link NativeEvent#BUTTON_RIGHT}. */ int getMouseButton(); /** * @return The key signal type of this even, or null if it is not a key event * @see KeySignalType */ KeySignalType getKeySignalType(); /** * Prevent this event from propagating or, if that is impossible, ensures that * {@link SignalEventImpl#create(Event, boolean)} will subsequently return null * for the same corresponding event object. */ void stopPropagation(); /** * Prevents the browser from taking its default action for the given event. * Under some circumstances (see {@link SignalEventImpl#isPreventDefaultEffective} * this may also stop propagation. * * See also {@link #stopPropagation()}. */ public void preventDefault(); }