blob: ad8d8e3c5188f0efe30812652443d4f6159a8fc8 [file] [log] [blame]
/*
* 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 com.google.gwt.user.client;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.HasNativeEvent;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
/**
* <p>
* An opaque handle to a native DOM Event. An <code>Event</code> cannot be
* created directly. Instead, use the <code>Event</code> type when returning a
* native DOM event from JSNI methods. An <code>Event</code> passed back into
* JSNI becomes the original DOM event the <code>Event</code> was created from,
* and can be accessed in JavaScript code as expected. This is typically done by
* calling methods in the {@link com.google.gwt.user.client.DOM} class.
* </p>
*/
public class Event extends NativeEvent {
/**
* Represents a preview of a native {@link Event}.
*/
public static class NativePreviewEvent extends GwtEvent<NativePreviewHandler>
implements HasNativeEvent {
/**
* Handler type.
*/
private static Type<NativePreviewHandler> TYPE;
/**
* The singleton instance of {@link NativePreviewEvent}.
*/
private static NativePreviewEvent singleton;
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<NativePreviewHandler> getType() {
if (TYPE == null) {
TYPE = new Type<NativePreviewHandler>();
}
return TYPE;
}
/**
* Fire a {@link NativePreviewEvent} for the native event.
*
* @param handlers the {@link HandlerManager}
* @param nativeEvent the native event
* @return true to fire the event normally, false to cancel the event
*/
private static boolean fire(HandlerManager handlers, NativeEvent nativeEvent) {
if (TYPE != null && handlers != null && handlers.isEventHandled(TYPE)) {
// Cache the current values in the singleton in case we are in the
// middle of handling another event.
boolean lastIsCanceled = singleton.isCanceled;
boolean lastIsConsumed = singleton.isConsumed;
boolean lastIsFirstHandler = singleton.isFirstHandler;
NativeEvent lastNativeEvent = singleton.nativeEvent;
// Revive the event
singleton.revive();
singleton.setNativeEvent(nativeEvent);
// Fire the event
handlers.fireEvent(singleton);
boolean ret = !(singleton.isCanceled() && !singleton.isConsumed());
// Restore the state of the singleton.
singleton.isCanceled = lastIsCanceled;
singleton.isConsumed = lastIsConsumed;
singleton.isFirstHandler = lastIsFirstHandler;
singleton.nativeEvent = lastNativeEvent;
return ret;
}
return true;
}
/**
* A boolean indicating that the native event should be canceled.
*/
private boolean isCanceled = false;
/**
* A boolean indicating whether or not canceling the native event should be
* prevented. This supercedes {@link #isCanceled}.
*/
private boolean isConsumed = false;
/**
* A boolean indicating that the current handler is at the top of the event
* preview stack.
*/
private boolean isFirstHandler = false;
/**
* The event being previewed.
*/
private NativeEvent nativeEvent;
/**
* Cancel the native event and prevent it from firing. Note that the event
* can still fire if another handler calls {@link #consume()}.
*
* Classes overriding this method should still call super.cancel().
*/
public void cancel() {
isCanceled = true;
}
/**
* Consume the native event and prevent it from being canceled, even if it
* has already been canceled by another handler.
* {@link NativePreviewHandler} that fire first have priority over later
* handlers, so all handlers should check if the event has already been
* canceled before calling this method.
*/
public void consume() {
isConsumed = true;
}
@Override
public final Type<NativePreviewHandler> getAssociatedType() {
return TYPE;
}
public NativeEvent getNativeEvent() {
return nativeEvent;
}
/**
* Gets the type int corresponding to the native event that triggered this
* preview.
*
* @return the type int associated with this native event
*/
public final int getTypeInt() {
return Event.as(getNativeEvent()).getTypeInt();
}
/**
* Has the event already been canceled? Note that {@link #isConsumed()} will
* still return true if the native event has also been consumed.
*
* @return true if the event has been canceled
* @see #cancel()
*/
public boolean isCanceled() {
return isCanceled;
}
/**
* Has the native event been consumed? Note that {@link #isCanceled()} will
* still return true if the native event has also been canceled.
*
* @return true if the event has been consumed
* @see #consume()
*/
public boolean isConsumed() {
return isConsumed;
}
/**
* Is the current handler the first to preview this event?
*
* @return true if the current handler is the first to preview the event
*/
public boolean isFirstHandler() {
return isFirstHandler;
}
@Override
protected void dispatch(NativePreviewHandler handler) {
handler.onPreviewNativeEvent(this);
singleton.isFirstHandler = false;
}
@Override
protected void revive() {
super.revive();
isCanceled = false;
isConsumed = false;
isFirstHandler = true;
nativeEvent = null;
}
/**
* Set the native event.
*
* @param nativeEvent the native {@link Event} being previewed.
*/
private void setNativeEvent(NativeEvent nativeEvent) {
this.nativeEvent = nativeEvent;
}
}
/**
* Handler interface for {@link NativePreviewEvent} events.
*/
public static interface NativePreviewHandler extends EventHandler {
/**
* Called when {@link NativePreviewEvent} is fired.
*
* @param event the {@link NativePreviewEvent} that was fired
*/
void onPreviewNativeEvent(NativePreviewEvent event);
}
/**
* Fired when an element loses keyboard focus.
*/
public static final int ONBLUR = 0x01000;
/**
* Fired when the value of an input element changes.
*/
public static final int ONCHANGE = 0x00400;
/**
* Fired when the user clicks on an element.
*/
public static final int ONCLICK = 0x00001;
/**
* Fired when the user double-clicks on an element.
*/
public static final int ONDBLCLICK = 0x00002;
/**
* Fired when an image encounters an error.
*/
public static final int ONERROR = 0x10000;
/**
* Fired when an element receives keyboard focus.
*/
public static final int ONFOCUS = 0x00800;
/**
* Fired when the user gesture changes.
*/
public static final int ONGESTURECHANGE = 0x2000000;
/**
* Fired when the user gesture ends.
*/
public static final int ONGESTUREEND = 0x4000000;
/**
* Fired when the user gesture starts.
*/
public static final int ONGESTURESTART = 0x1000000;
/**
* Fired when the user depresses a key.
*/
public static final int ONKEYDOWN = 0x00080;
/**
* Fired when the a character is generated from a keypress (either directly or
* through auto-repeat).
*/
public static final int ONKEYPRESS = 0x00100;
/**
* Fired when the user releases a key.
*/
public static final int ONKEYUP = 0x00200;
/**
* Fired when an element (normally an IMG) finishes loading.
*/
public static final int ONLOAD = 0x08000;
/**
* Fired when an element that has mouse capture loses it.
*/
public static final int ONLOSECAPTURE = 0x02000;
/**
* Fired when the user depresses a mouse button over an element.
*/
public static final int ONMOUSEDOWN = 0x00004;
/**
* Fired when the mouse is moved within an element's area.
*/
public static final int ONMOUSEMOVE = 0x00040;
/**
* Fired when the mouse is moved out of an element's area.
*/
public static final int ONMOUSEOUT = 0x00020;
/**
* Fired when the mouse is moved into an element's area.
*/
public static final int ONMOUSEOVER = 0x00010;
/**
* Fired when the user releases a mouse button over an element.
*/
public static final int ONMOUSEUP = 0x00008;
/**
* Fired when the user scrolls the mouse wheel over an element.
*/
public static final int ONMOUSEWHEEL = 0x20000;
/**
* Fired when the user pastes text into an input element.
*/
public static final int ONPASTE = 0x80000;
/**
* Fired when a scrollable element's scroll offset changes.
*/
public static final int ONSCROLL = 0x04000;
/**
* Fired when the user cancels touching an element.
*/
public static final int ONTOUCHCANCEL = 0x800000;
/**
* Fired when the user ends touching an element.
*/
public static final int ONTOUCHEND = 0x400000;
/**
* Fired when the user moves while touching an element.
*/
public static final int ONTOUCHMOVE = 0x200000;
/**
* Fired when the user starts touching an element.
*/
public static final int ONTOUCHSTART = 0x100000;
/**
* Fired when the user requests an element's context menu (usually by
* right-clicking).
*/
public static final int ONCONTEXTMENU = 0x40000;
/**
* A bit-mask covering both focus events (focus and blur).
*/
public static final int FOCUSEVENTS = ONFOCUS | ONBLUR;
/**
* A bit-mask covering all keyboard events (down, up, and press).
*/
public static final int KEYEVENTS = ONKEYDOWN | ONKEYPRESS | ONKEYUP;
/**
* A bit-mask covering all mouse events (down, up, move, over, and out), but
* not click, dblclick, or wheel events.
*/
public static final int MOUSEEVENTS = ONMOUSEDOWN | ONMOUSEUP | ONMOUSEMOVE
| ONMOUSEOVER | ONMOUSEOUT;
/**
* A bit-mask covering all touch events (start, move, end, cancel).
*/
public static final int TOUCHEVENTS = ONTOUCHSTART | ONTOUCHMOVE | ONTOUCHEND | ONTOUCHCANCEL;
/**
* A bit-mask covering all gesture events (start, change, end).
*/
public static final int GESTUREEVENTS = ONGESTURESTART | ONGESTURECHANGE | ONGESTUREEND;
/**
* Value returned by accessors when the actual integer value is undefined. In
* Development Mode, most accessors assert that the requested attribute is
* reliable across all supported browsers.
*
* @see Event
*/
@Deprecated
public static final int UNDEFINED = 0;
/**
* The list of {@link NativePreviewHandler}. We use a list instead of a
* handler manager for efficiency and because we want to fire the handlers in
* reverse order. When the last handler is removed, handlers is reset to null.
*/
static HandlerManager handlers;
/**
* Adds an event preview to the preview stack. As long as this preview remains
* on the top of the stack, it will receive all events before they are fired
* to their listeners. Note that the event preview will receive <u>all </u>
* events, including those received due to bubbling, whereas normal event
* handlers only receive explicitly sunk events.
*
* @param preview the event preview to be added to the stack.
* @deprecated replaced by
* {@link #addNativePreviewHandler(NativePreviewHandler)}
*/
@Deprecated
public static void addEventPreview(EventPreview preview) {
DOM.addEventPreview(preview);
}
/**
* <p>
* Adds a {@link NativePreviewHandler} that will receive all events before
* they are fired to their handlers. Note that the handler will receive
* <u>all</u> native events, including those received due to bubbling, whereas
* normal event handlers only receive explicitly sunk events.
* </p>
*
* <p>
* Unlike other event handlers, {@link NativePreviewHandler} are fired in the
* reverse order that they are added, such that the last
* {@link NativePreviewEvent} that was added is the first to be fired.
* </p>
*
* <p>
* Please note that nondeterministic behavior will result if more than one GWT
* application registers preview handlers. See <a href=
* 'http://code.google.com/p/google-web-toolkit/issues/detail?id=3892'>issue
* 3892</a> for details.
* </p>
*
* @param handler the {@link NativePreviewHandler}
* @return {@link HandlerRegistration} used to remove this handler
*/
public static HandlerRegistration addNativePreviewHandler(
final NativePreviewHandler handler) {
assert handler != null : "Cannot add a null handler";
DOM.maybeInitializeEventSystem();
// Initialize the type
NativePreviewEvent.getType();
if (handlers == null) {
handlers = new HandlerManager(null, true);
NativePreviewEvent.singleton = new NativePreviewEvent();
}
return handlers.addHandler(NativePreviewEvent.TYPE, handler);
}
/**
* Converts the {@link NativeEvent} to Event. This is always safe.
*
* @param event the event to downcast
*/
public static Event as(NativeEvent event) {
return (Event) event;
}
/**
* Fire a {@link NativePreviewEvent} for the native event.
*
* @param nativeEvent the native event
* @return true to fire the event normally, false to cancel the event
*/
public static boolean fireNativePreviewEvent(NativeEvent nativeEvent) {
return NativePreviewEvent.fire(handlers, nativeEvent);
}
/**
* Gets the current event that is being fired. The current event is only
* available within the lifetime of the onBrowserEvent function. Once the
* onBrowserEvent method returns, the current event is reset to null.
*
* @return the current event
*/
public static Event getCurrentEvent() {
return DOM.eventGetCurrentEvent();
}
/**
* Gets the {@link EventListener} that will receive events for the given
* element. Only one such listener may exist for a single element.
*
* @param elem the element whose listener is to be set
* @return the element's event listener
*/
public static EventListener getEventListener(Element elem) {
return DOM.getEventListener(elem);
}
/**
* Gets the current set of events sunk by a given element.
*
* @param elem the element whose events are to be retrieved
* @return a bitfield describing the events sunk on this element (its possible
* values are described in {@link Event})
*/
public static int getEventsSunk(Element elem) {
return DOM.getEventsSunk(elem);
}
/**
* Gets the enumerated type of this event given a valid event type name.
*
* @param typeName the typeName to be tested
* @return the event's enumerated type, or -1 if not defined
*/
public static int getTypeInt(String typeName) {
return DOM.impl.eventGetTypeInt(typeName);
}
/**
* Releases mouse capture on the given element. Calling this method has no
* effect if the element does not currently have mouse capture.
*
* @param elem the element to release capture
* @see #setCapture(Element)
*/
public static void releaseCapture(Element elem) {
DOM.releaseCapture(elem);
}
/**
* Removes an element from the preview stack. This element will no longer
* capture events, though any preview underneath it will begin to do so.
*
* @param preview the event preview to be removed from the stack
* @deprecated use {@link HandlerRegistration} returned from
* {@link Event#addNativePreviewHandler(NativePreviewHandler)}
*/
@Deprecated
public static void removeEventPreview(EventPreview preview) {
DOM.removeEventPreview(preview);
}
/**
* Sets mouse-capture on the given element. This element will directly receive
* all mouse events until {@link #releaseCapture(Element)} is called on it.
*
* @param elem the element on which to set mouse capture
*/
public static void setCapture(Element elem) {
DOM.setCapture(elem);
}
/**
* Sets the {@link EventListener} to receive events for the given element.
* Only one such listener may exist for a single element.
*
* @param elem the element whose listener is to be set
* @param listener the listener to receive {@link Event events}
*/
public static void setEventListener(Element elem, EventListener listener) {
DOM.setEventListener(elem, listener);
}
/**
* Sets the current set of events sunk by a given element. These events will
* be fired to the nearest {@link EventListener} specified on any of the
* element's parents.
*
* @param elem the element whose events are to be retrieved
* @param eventBits a bitfield describing the events sunk on this element (its
* possible values are described in {@link Event})
*/
public static void sinkEvents(Element elem, int eventBits) {
DOM.sinkEvents(elem, eventBits);
}
/**
* Not directly instantiable. Subclasses should also define a protected no-arg
* constructor to prevent client code from directly instantiating the class.
*/
protected Event() {
}
/**
* Cancels bubbling for the given event. This will stop the event from being
* propagated to parent elements.
*
* @param cancel <code>true</code> to cancel bubbling
* @deprecated use {@link NativeEvent#stopPropagation()} instead
*/
@Deprecated
public final void cancelBubble(boolean cancel) {
DOM.eventCancelBubble(this, cancel);
}
/**
* Gets the current target element of this event. This is the element whose
* listener fired last, not the element which fired the event initially.
*
* @return the event's current target element
* @deprecated use {@link NativeEvent#getCurrentEventTarget()} instead
*/
@Deprecated
public final Element getCurrentTarget() {
return getCurrentEventTarget().cast();
}
/**
* Gets the element from which the mouse pointer was moved (only valid for
* {@link Event#ONMOUSEOVER}).
*
* @deprecated use {@link NativeEvent#getRelatedEventTarget()} instead
* @return the element from which the mouse pointer was moved
*/
@Deprecated
public final Element getFromElement() {
return DOM.eventGetFromElement(this);
}
/**
* Gets the related target for this event.
*
* @return the related target
* @deprecated use {@link NativeEvent#getRelatedEventTarget()} instead
*/
@Deprecated
public final Element getRelatedTarget() {
return getRelatedEventTarget().cast();
}
/**
* Gets the key-repeat state of this event.
*
* @return <code>true</code> if this key event was an auto-repeat
* @deprecated not supported on all browsers
*/
@Deprecated
public final boolean getRepeat() {
return DOM.eventGetRepeat(this);
}
/**
* Returns the element that was the actual target of the given event.
*
* @return the target element
* @deprecated use {@link NativeEvent#getEventTarget()} instead
*/
@Deprecated
public final Element getTarget() {
return getEventTarget().cast();
}
/**
* Gets the element to which the mouse pointer was moved (only valid for
* {@link Event#ONMOUSEOUT}).
*
* @deprecated use {@link NativeEvent#getRelatedEventTarget()} instead
* @return the element to which the mouse pointer was moved
*/
@Deprecated
public final Element getToElement() {
return DOM.eventGetToElement(this);
}
/**
* Gets the enumerated type of this event, as defined by {@link #ONCLICK},
* {@link #ONMOUSEDOWN}, and so forth.
*
* @return the event's enumerated type
*/
public final int getTypeInt() {
return DOM.eventGetType(this);
}
}