blob: d911fd4bdfa58ef519031b7b58915098f57932f7 [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.event.dom.client;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HasHandlers;
import java.util.ArrayList;
import java.util.List;
/**
* {@link DomEvent} is a subclass of {@link GwtEvent} that provides events that
* underlying native browser event object as well as a subclass of {@link Type}
* that understands GWT event bits used by sinkEvents().
*
* @param <H> handler type
*
*/
public abstract class DomEvent<H extends EventHandler> extends GwtEvent<H>
implements HasNativeEvent {
/**
* Type class used by dom event subclasses. Type is specialized for dom in
* order to carry information about the native event.
*
* @param <H> handler type
*/
public static class Type<H extends EventHandler> extends GwtEvent.Type<H> {
private DomEvent<H> flyweight;
private String name;
/**
* This constructor allows dom event types to be triggered by the
* {@link DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent, HasHandlers)}
* method. It should only be used by implementors supporting new dom events.
*
* <p>
* Any such dom event type must act as a flyweight around a native event
* object.
* </p>
*
* @param eventName the raw native event name
* @param flyweight the instance that will be used as a flyweight to wrap a
* native event
*/
public Type(String eventName, DomEvent<H> flyweight) {
this.flyweight = flyweight;
// Until we have eager clinits implemented, we are manually initializing
// DomEvent here.
if (registered == null) {
init();
}
List<Type<?>> types = registered.unsafeGet(eventName);
if (types == null) {
types = new ArrayList<Type<?>>();
registered.unsafePut(eventName, types);
}
types.add(this);
name = eventName;
}
/**
* Gets the name associated with this event type.
*
* @return the name of this event type
*/
public String getName() {
return name;
}
}
private static PrivateMap<List<Type<?>>> registered;
/**
* Fires the given native event on the specified handlers.
*
* @param nativeEvent the native event
* @param handlerSource the source of the handlers to fire
*/
public static void fireNativeEvent(NativeEvent nativeEvent,
HasHandlers handlerSource) {
fireNativeEvent(nativeEvent, handlerSource, null);
}
/**
* Fires the given native event on the specified handlers.
*
* @param nativeEvent the native event
* @param handlerSource the source of the handlers to fire
* @param relativeElem the element relative to which event coordinates will be
* measured
*/
public static void fireNativeEvent(NativeEvent nativeEvent,
HasHandlers handlerSource, Element relativeElem) {
assert nativeEvent != null : "nativeEvent must not be null";
if (registered != null) {
List<Type<?>> types = registered.unsafeGet(nativeEvent.getType());
if (types != null) {
for (DomEvent.Type<?> type : types) {
// Store and restore native event just in case we are in recursive
// loop.
NativeEvent currentNative = type.flyweight.nativeEvent;
Element currentRelativeElem = type.flyweight.relativeElem;
type.flyweight.setNativeEvent(nativeEvent);
type.flyweight.setRelativeElement(relativeElem);
handlerSource.fireEvent(type.flyweight);
type.flyweight.setNativeEvent(currentNative);
type.flyweight.setRelativeElement(currentRelativeElem);
}
}
}
}
// This method can go away once we have eager clinits.
static void init() {
registered = new PrivateMap<List<Type<?>>>();
}
private NativeEvent nativeEvent;
private Element relativeElem;
@Override
public abstract DomEvent.Type<H> getAssociatedType();
public final NativeEvent getNativeEvent() {
assertLive();
return nativeEvent;
}
/**
* Gets the element relative to which event coordinates will be measured.
* If this element is <code>null</code>, event coordinates will be measured
* relative to the window's client area.
*
* @return the event's relative element
*/
public final Element getRelativeElement() {
assertLive();
return relativeElem;
}
/**
* Prevents the wrapped native event's default action.
*/
public void preventDefault() {
assertLive();
// If there is no native event object set, do nothing. There will be no
// default action anyway.
if (nativeEvent != null) {
nativeEvent.preventDefault();
}
}
/**
* Sets the native event associated with this dom event. In general, dom
* events should be fired using the static firing methods.
*
* @param nativeEvent the native event
*/
public final void setNativeEvent(NativeEvent nativeEvent) {
this.nativeEvent = nativeEvent;
}
/**
* Gets the element relative to which event coordinates will be measured.
*
* @param relativeElem the event's relative element
*/
public void setRelativeElement(Element relativeElem) {
this.relativeElem = relativeElem;
}
/**
* Stops the propagation of the underlying native event.
*/
public void stopPropagation() {
assertLive();
nativeEvent.stopPropagation();
}
}