/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.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();
/**
* @return true if it is a DOMCharacterDataModified event
*/
boolean isDOMCharacterEvent();
}