summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/event
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/event')
-rw-r--r--server/src/com/vaadin/event/Action.java201
-rw-r--r--server/src/com/vaadin/event/ActionManager.java261
-rw-r--r--server/src/com/vaadin/event/ComponentEventListener.java23
-rw-r--r--server/src/com/vaadin/event/DataBoundTransferable.java78
-rw-r--r--server/src/com/vaadin/event/EventRouter.java211
-rw-r--r--server/src/com/vaadin/event/FieldEvents.java287
-rw-r--r--server/src/com/vaadin/event/ItemClickEvent.java133
-rw-r--r--server/src/com/vaadin/event/LayoutEvents.java150
-rw-r--r--server/src/com/vaadin/event/ListenerMethod.java671
-rw-r--r--server/src/com/vaadin/event/MethodEventSource.java167
-rw-r--r--server/src/com/vaadin/event/MouseEvents.java236
-rw-r--r--server/src/com/vaadin/event/ShortcutAction.java384
-rw-r--r--server/src/com/vaadin/event/ShortcutListener.java45
-rw-r--r--server/src/com/vaadin/event/Transferable.java69
-rw-r--r--server/src/com/vaadin/event/TransferableImpl.java59
-rw-r--r--server/src/com/vaadin/event/dd/DragAndDropEvent.java62
-rw-r--r--server/src/com/vaadin/event/dd/DragSource.java64
-rw-r--r--server/src/com/vaadin/event/dd/DropHandler.java73
-rw-r--r--server/src/com/vaadin/event/dd/DropTarget.java54
-rw-r--r--server/src/com/vaadin/event/dd/TargetDetails.java49
-rw-r--r--server/src/com/vaadin/event/dd/TargetDetailsImpl.java58
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java48
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/AcceptCriterion.java87
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/And.java66
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/ClientSideCriterion.java73
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java65
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/Not.java51
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/Or.java64
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java69
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java79
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java63
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java84
-rw-r--r--server/src/com/vaadin/event/package.html58
33 files changed, 4142 insertions, 0 deletions
diff --git a/server/src/com/vaadin/event/Action.java b/server/src/com/vaadin/event/Action.java
new file mode 100644
index 0000000000..aca28940a1
--- /dev/null
+++ b/server/src/com/vaadin/event/Action.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.Resource;
+
+/**
+ * Implements the action framework. This class contains subinterfaces for action
+ * handling and listing, and for action handler registrations and
+ * unregistration.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+@SuppressWarnings("serial")
+public class Action implements Serializable {
+
+ /**
+ * Action title.
+ */
+ private String caption;
+
+ /**
+ * Action icon.
+ */
+ private Resource icon = null;
+
+ /**
+ * Constructs a new action with the given caption.
+ *
+ * @param caption
+ * the caption for the new action.
+ */
+ public Action(String caption) {
+ this.caption = caption;
+ }
+
+ /**
+ * Constructs a new action with the given caption string and icon.
+ *
+ * @param caption
+ * the caption for the new action.
+ * @param icon
+ * the icon for the new action.
+ */
+ public Action(String caption, Resource icon) {
+ this.caption = caption;
+ this.icon = icon;
+ }
+
+ /**
+ * Returns the action's caption.
+ *
+ * @return the action's caption as a <code>String</code>.
+ */
+ public String getCaption() {
+ return caption;
+ }
+
+ /**
+ * Returns the action's icon.
+ *
+ * @return the action's Icon.
+ */
+ public Resource getIcon() {
+ return icon;
+ }
+
+ /**
+ * An Action that implements this interface can be added to an
+ * Action.Notifier (or NotifierProxy) via the <code>addAction()</code>
+ * -method, which in many cases is easier than implementing the
+ * Action.Handler interface.<br/>
+ *
+ */
+ public interface Listener extends Serializable {
+ public void handleAction(Object sender, Object target);
+ }
+
+ /**
+ * Action.Containers implementing this support an easier way of adding
+ * single Actions than the more involved Action.Handler. The added actions
+ * must be Action.Listeners, thus handling the action themselves.
+ *
+ */
+ public interface Notifier extends Container {
+ public <T extends Action & Action.Listener> void addAction(T action);
+
+ public <T extends Action & Action.Listener> void removeAction(T action);
+ }
+
+ public interface ShortcutNotifier extends Serializable {
+ public void addShortcutListener(ShortcutListener shortcut);
+
+ public void removeShortcutListener(ShortcutListener shortcut);
+ }
+
+ /**
+ * Interface implemented by classes who wish to handle actions.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+ public interface Handler extends Serializable {
+
+ /**
+ * Gets the list of actions applicable to this handler.
+ *
+ * @param target
+ * the target handler to list actions for. For item
+ * containers this is the item id.
+ * @param sender
+ * the party that would be sending the actions. Most of this
+ * is the action container.
+ * @return the list of Action
+ */
+ public Action[] getActions(Object target, Object sender);
+
+ /**
+ * Handles an action for the given target. The handler method may just
+ * discard the action if it's not suitable.
+ *
+ * @param action
+ * the action to be handled.
+ * @param sender
+ * the sender of the action. This is most often the action
+ * container.
+ * @param target
+ * the target of the action. For item containers this is the
+ * item id.
+ */
+ public void handleAction(Action action, Object sender, Object target);
+ }
+
+ /**
+ * Interface implemented by all components where actions can be registered.
+ * This means that the components lets others to register as action handlers
+ * to it. When the component receives an action targeting its contents it
+ * should loop all action handlers registered to it and let them handle the
+ * action.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+ public interface Container extends Serializable {
+
+ /**
+ * Registers a new action handler for this container
+ *
+ * @param actionHandler
+ * the new handler to be added.
+ */
+ public void addActionHandler(Action.Handler actionHandler);
+
+ /**
+ * Removes a previously registered action handler for the contents of
+ * this container.
+ *
+ * @param actionHandler
+ * the handler to be removed.
+ */
+ public void removeActionHandler(Action.Handler actionHandler);
+ }
+
+ /**
+ * Sets the caption.
+ *
+ * @param caption
+ * the caption to set.
+ */
+ public void setCaption(String caption) {
+ this.caption = caption;
+ }
+
+ /**
+ * Sets the icon.
+ *
+ * @param icon
+ * the icon to set.
+ */
+ public void setIcon(Resource icon) {
+ this.icon = icon;
+ }
+
+}
diff --git a/server/src/com/vaadin/event/ActionManager.java b/server/src/com/vaadin/event/ActionManager.java
new file mode 100644
index 0000000000..50ddef6265
--- /dev/null
+++ b/server/src/com/vaadin/event/ActionManager.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.util.HashSet;
+import java.util.Map;
+
+import com.vaadin.event.Action.Container;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.terminal.KeyMapper;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.VariableOwner;
+import com.vaadin.ui.Component;
+
+/**
+ * Javadoc TODO
+ *
+ * Notes:
+ * <p>
+ * Empties the keymapper for each repaint to avoid leaks; can cause problems in
+ * the future if the client assumes key don't change. (if lazyloading, one must
+ * not cache results)
+ * </p>
+ *
+ *
+ */
+public class ActionManager implements Action.Container, Action.Handler,
+ Action.Notifier {
+
+ private static final long serialVersionUID = 1641868163608066491L;
+
+ /** List of action handlers */
+ protected HashSet<Action> ownActions = null;
+
+ /** List of action handlers */
+ protected HashSet<Handler> actionHandlers = null;
+
+ /** Action mapper */
+ protected KeyMapper<Action> actionMapper = null;
+
+ protected Component viewer;
+
+ private boolean clientHasActions = false;
+
+ public ActionManager() {
+
+ }
+
+ public <T extends Component & Container & VariableOwner> ActionManager(
+ T viewer) {
+ this.viewer = viewer;
+ }
+
+ private void requestRepaint() {
+ if (viewer != null) {
+ viewer.requestRepaint();
+ }
+ }
+
+ public <T extends Component & Container & VariableOwner> void setViewer(
+ T viewer) {
+ if (viewer == this.viewer) {
+ return;
+ }
+ if (this.viewer != null) {
+ ((Container) this.viewer).removeActionHandler(this);
+ }
+ requestRepaint(); // this goes to the old viewer
+ if (viewer != null) {
+ viewer.addActionHandler(this);
+ }
+ this.viewer = viewer;
+ requestRepaint(); // this goes to the new viewer
+ }
+
+ @Override
+ public <T extends Action & Action.Listener> void addAction(T action) {
+ if (ownActions == null) {
+ ownActions = new HashSet<Action>();
+ }
+ if (ownActions.add(action)) {
+ requestRepaint();
+ }
+ }
+
+ @Override
+ public <T extends Action & Action.Listener> void removeAction(T action) {
+ if (ownActions != null) {
+ if (ownActions.remove(action)) {
+ requestRepaint();
+ }
+ }
+ }
+
+ @Override
+ public void addActionHandler(Handler actionHandler) {
+ if (actionHandler == this) {
+ // don't add the actionHandler to itself
+ return;
+ }
+ if (actionHandler != null) {
+
+ if (actionHandlers == null) {
+ actionHandlers = new HashSet<Handler>();
+ }
+
+ if (actionHandlers.add(actionHandler)) {
+ requestRepaint();
+ }
+ }
+ }
+
+ @Override
+ public void removeActionHandler(Action.Handler actionHandler) {
+ if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
+
+ if (actionHandlers.remove(actionHandler)) {
+ requestRepaint();
+ }
+ if (actionHandlers.isEmpty()) {
+ actionHandlers = null;
+ }
+
+ }
+ }
+
+ public void removeAllActionHandlers() {
+ if (actionHandlers != null) {
+ actionHandlers = null;
+ requestRepaint();
+ }
+ }
+
+ public void paintActions(Object actionTarget, PaintTarget paintTarget)
+ throws PaintException {
+
+ actionMapper = null;
+
+ HashSet<Action> actions = new HashSet<Action>();
+ if (actionHandlers != null) {
+ for (Action.Handler handler : actionHandlers) {
+ Action[] as = handler.getActions(actionTarget, viewer);
+ if (as != null) {
+ for (Action action : as) {
+ actions.add(action);
+ }
+ }
+ }
+ }
+ if (ownActions != null) {
+ actions.addAll(ownActions);
+ }
+
+ /*
+ * Must repaint whenever there are actions OR if all actions have been
+ * removed but still exist on client side
+ */
+ if (!actions.isEmpty() || clientHasActions) {
+ actionMapper = new KeyMapper<Action>();
+
+ paintTarget.addVariable((VariableOwner) viewer, "action", "");
+ paintTarget.startTag("actions");
+
+ for (final Action a : actions) {
+ paintTarget.startTag("action");
+ final String akey = actionMapper.key(a);
+ paintTarget.addAttribute("key", akey);
+ if (a.getCaption() != null) {
+ paintTarget.addAttribute("caption", a.getCaption());
+ }
+ if (a.getIcon() != null) {
+ paintTarget.addAttribute("icon", a.getIcon());
+ }
+ if (a instanceof ShortcutAction) {
+ final ShortcutAction sa = (ShortcutAction) a;
+ paintTarget.addAttribute("kc", sa.getKeyCode());
+ final int[] modifiers = sa.getModifiers();
+ if (modifiers != null) {
+ final String[] smodifiers = new String[modifiers.length];
+ for (int i = 0; i < modifiers.length; i++) {
+ smodifiers[i] = String.valueOf(modifiers[i]);
+ }
+ paintTarget.addAttribute("mk", smodifiers);
+ }
+ }
+ paintTarget.endTag("action");
+ }
+
+ paintTarget.endTag("actions");
+ }
+
+ /*
+ * Update flag for next repaint so we know if we need to paint empty
+ * actions or not (must send actions is client had actions before and
+ * all actions were removed).
+ */
+ clientHasActions = !actions.isEmpty();
+ }
+
+ public void handleActions(Map<String, Object> variables, Container sender) {
+ if (variables.containsKey("action") && actionMapper != null) {
+ final String key = (String) variables.get("action");
+ final Action action = actionMapper.get(key);
+ final Object target = variables.get("actiontarget");
+ if (action != null) {
+ handleAction(action, sender, target);
+ }
+ }
+ }
+
+ @Override
+ public Action[] getActions(Object target, Object sender) {
+ HashSet<Action> actions = new HashSet<Action>();
+ if (ownActions != null) {
+ for (Action a : ownActions) {
+ actions.add(a);
+ }
+ }
+ if (actionHandlers != null) {
+ for (Action.Handler h : actionHandlers) {
+ Action[] as = h.getActions(target, sender);
+ if (as != null) {
+ for (Action a : as) {
+ actions.add(a);
+ }
+ }
+ }
+ }
+ return actions.toArray(new Action[actions.size()]);
+ }
+
+ @Override
+ public void handleAction(Action action, Object sender, Object target) {
+ if (actionHandlers != null) {
+ Handler[] array = actionHandlers.toArray(new Handler[actionHandlers
+ .size()]);
+ for (Handler handler : array) {
+ handler.handleAction(action, sender, target);
+ }
+ }
+ if (ownActions != null && ownActions.contains(action)
+ && action instanceof Action.Listener) {
+ ((Action.Listener) action).handleAction(sender, target);
+ }
+ }
+
+}
diff --git a/server/src/com/vaadin/event/ComponentEventListener.java b/server/src/com/vaadin/event/ComponentEventListener.java
new file mode 100644
index 0000000000..820697ac9c
--- /dev/null
+++ b/server/src/com/vaadin/event/ComponentEventListener.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.util.EventListener;
+
+public interface ComponentEventListener extends EventListener, Serializable {
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/DataBoundTransferable.java b/server/src/com/vaadin/event/DataBoundTransferable.java
new file mode 100644
index 0000000000..653997d11f
--- /dev/null
+++ b/server/src/com/vaadin/event/DataBoundTransferable.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.util.Map;
+
+import com.vaadin.data.Container;
+import com.vaadin.ui.Component;
+
+/**
+ * Parent class for {@link Transferable} implementations that have a Vaadin
+ * container as a data source. The transfer is associated with an item
+ * (identified by its Id) and optionally also a property identifier (e.g. a
+ * table column identifier when transferring a single table cell).
+ *
+ * The component must implement the interface
+ * {@link com.vaadin.data.Container.Viewer}.
+ *
+ * In most cases, receivers of data transfers should depend on this class
+ * instead of its concrete subclasses.
+ *
+ * @since 6.3
+ */
+public abstract class DataBoundTransferable extends TransferableImpl {
+
+ public DataBoundTransferable(Component sourceComponent,
+ Map<String, Object> rawVariables) {
+ super(sourceComponent, rawVariables);
+ }
+
+ /**
+ * Returns the identifier of the item being transferred.
+ *
+ * @return item identifier
+ */
+ public abstract Object getItemId();
+
+ /**
+ * Returns the optional property identifier that the transfer concerns.
+ *
+ * This can be e.g. the table column from which a drag operation originated.
+ *
+ * @return property identifier
+ */
+ public abstract Object getPropertyId();
+
+ /**
+ * Returns the container data source from which the transfer occurs.
+ *
+ * {@link com.vaadin.data.Container.Viewer#getContainerDataSource()} is used
+ * to obtain the underlying container of the source component.
+ *
+ * @return Container
+ */
+ public Container getSourceContainer() {
+ Component sourceComponent = getSourceComponent();
+ if (sourceComponent instanceof Container.Viewer) {
+ return ((Container.Viewer) sourceComponent)
+ .getContainerDataSource();
+ } else {
+ // this should not happen
+ return null;
+ }
+ }
+}
diff --git a/server/src/com/vaadin/event/EventRouter.java b/server/src/com/vaadin/event/EventRouter.java
new file mode 100644
index 0000000000..80c6e5294d
--- /dev/null
+++ b/server/src/com/vaadin/event/EventRouter.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EventObject;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+/**
+ * <code>EventRouter</code> class implementing the inheritable event listening
+ * model. For more information on the event model see the
+ * {@link com.vaadin.event package documentation}.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+@SuppressWarnings("serial")
+public class EventRouter implements MethodEventSource {
+
+ /**
+ * List of registered listeners.
+ */
+ private LinkedHashSet<ListenerMethod> listenerList = null;
+
+ /*
+ * Registers a new listener with the specified activation method to listen
+ * events generated by this component. Don't add a JavaDoc comment here, we
+ * use the default documentation from implemented interface.
+ */
+ @Override
+ public void addListener(Class<?> eventType, Object object, Method method) {
+ if (listenerList == null) {
+ listenerList = new LinkedHashSet<ListenerMethod>();
+ }
+ listenerList.add(new ListenerMethod(eventType, object, method));
+ }
+
+ /*
+ * Registers a new listener with the specified named activation method to
+ * listen events generated by this component. Don't add a JavaDoc comment
+ * here, we use the default documentation from implemented interface.
+ */
+ @Override
+ public void addListener(Class<?> eventType, Object object, String methodName) {
+ if (listenerList == null) {
+ listenerList = new LinkedHashSet<ListenerMethod>();
+ }
+ listenerList.add(new ListenerMethod(eventType, object, methodName));
+ }
+
+ /*
+ * Removes all registered listeners matching the given parameters. Don't add
+ * a JavaDoc comment here, we use the default documentation from implemented
+ * interface.
+ */
+ @Override
+ public void removeListener(Class<?> eventType, Object target) {
+ if (listenerList != null) {
+ final Iterator<ListenerMethod> i = listenerList.iterator();
+ while (i.hasNext()) {
+ final ListenerMethod lm = i.next();
+ if (lm.matches(eventType, target)) {
+ i.remove();
+ return;
+ }
+ }
+ }
+ }
+
+ /*
+ * Removes the event listener methods matching the given given paramaters.
+ * Don't add a JavaDoc comment here, we use the default documentation from
+ * implemented interface.
+ */
+ @Override
+ public void removeListener(Class<?> eventType, Object target, Method method) {
+ if (listenerList != null) {
+ final Iterator<ListenerMethod> i = listenerList.iterator();
+ while (i.hasNext()) {
+ final ListenerMethod lm = i.next();
+ if (lm.matches(eventType, target, method)) {
+ i.remove();
+ return;
+ }
+ }
+ }
+ }
+
+ /*
+ * Removes the event listener method matching the given given parameters.
+ * Don't add a JavaDoc comment here, we use the default documentation from
+ * implemented interface.
+ */
+ @Override
+ public void removeListener(Class<?> eventType, Object target,
+ String methodName) {
+
+ // Find the correct method
+ final Method[] methods = target.getClass().getMethods();
+ Method method = null;
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals(methodName)) {
+ method = methods[i];
+ }
+ }
+ if (method == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // Remove the listeners
+ if (listenerList != null) {
+ final Iterator<ListenerMethod> i = listenerList.iterator();
+ while (i.hasNext()) {
+ final ListenerMethod lm = i.next();
+ if (lm.matches(eventType, target, method)) {
+ i.remove();
+ return;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Removes all listeners from event router.
+ */
+ public void removeAllListeners() {
+ listenerList = null;
+ }
+
+ /**
+ * Sends an event to all registered listeners. The listeners will decide if
+ * the activation method should be called or not.
+ *
+ * @param event
+ * the Event to be sent to all listeners.
+ */
+ public void fireEvent(EventObject event) {
+ // It is not necessary to send any events if there are no listeners
+ if (listenerList != null) {
+
+ // Make a copy of the listener list to allow listeners to be added
+ // inside listener methods. Fixes #3605.
+
+ // Send the event to all listeners. The listeners themselves
+ // will filter out unwanted events.
+ final Object[] listeners = listenerList.toArray();
+ for (int i = 0; i < listeners.length; i++) {
+ ((ListenerMethod) listeners[i]).receiveEvent(event);
+ }
+
+ }
+ }
+
+ /**
+ * Checks if the given Event type is listened by a listener registered to
+ * this router.
+ *
+ * @param eventType
+ * the event type to be checked
+ * @return true if a listener is registered for the given event type
+ */
+ public boolean hasListeners(Class<?> eventType) {
+ if (listenerList != null) {
+ for (ListenerMethod lm : listenerList) {
+ if (lm.isType(eventType)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns all listeners that match or extend the given event type.
+ *
+ * @param eventType
+ * The type of event to return listeners for.
+ * @return A collection with all registered listeners. Empty if no listeners
+ * are found.
+ */
+ public Collection<?> getListeners(Class<?> eventType) {
+ List<Object> listeners = new ArrayList<Object>();
+ if (listenerList != null) {
+ for (ListenerMethod lm : listenerList) {
+ if (lm.isOrExtendsType(eventType)) {
+ listeners.add(lm.getTarget());
+ }
+ }
+ }
+ return listeners;
+ }
+}
diff --git a/server/src/com/vaadin/event/FieldEvents.java b/server/src/com/vaadin/event/FieldEvents.java
new file mode 100644
index 0000000000..8e247a6fef
--- /dev/null
+++ b/server/src/com/vaadin/event/FieldEvents.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import com.vaadin.shared.EventId;
+import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Component.Event;
+import com.vaadin.ui.Field;
+import com.vaadin.ui.Field.ValueChangeEvent;
+import com.vaadin.ui.TextField;
+
+/**
+ * Interface that serves as a wrapper for {@link Field} related events.
+ */
+public interface FieldEvents {
+
+ /**
+ * The interface for adding and removing <code>FocusEvent</code> listeners.
+ * By implementing this interface a class explicitly announces that it will
+ * generate a <code>FocusEvent</code> when it receives keyboard focus.
+ * <p>
+ * Note: The general Java convention is not to explicitly declare that a
+ * class generates events, but to directly define the
+ * <code>addListener</code> and <code>removeListener</code> methods. That
+ * way the caller of these methods has no real way of finding out if the
+ * class really will send the events, or if it just defines the methods to
+ * be able to implement an interface.
+ * </p>
+ *
+ * @since 6.2
+ * @see FocusListener
+ * @see FocusEvent
+ */
+ public interface FocusNotifier extends Serializable {
+ /**
+ * Adds a <code>FocusListener</code> to the Component which gets fired
+ * when a <code>Field</code> receives keyboard focus.
+ *
+ * @param listener
+ * @see FocusListener
+ * @since 6.2
+ */
+ public void addListener(FocusListener listener);
+
+ /**
+ * Removes a <code>FocusListener</code> from the Component.
+ *
+ * @param listener
+ * @see FocusListener
+ * @since 6.2
+ */
+ public void removeListener(FocusListener listener);
+ }
+
+ /**
+ * The interface for adding and removing <code>BlurEvent</code> listeners.
+ * By implementing this interface a class explicitly announces that it will
+ * generate a <code>BlurEvent</code> when it loses keyboard focus.
+ * <p>
+ * Note: The general Java convention is not to explicitly declare that a
+ * class generates events, but to directly define the
+ * <code>addListener</code> and <code>removeListener</code> methods. That
+ * way the caller of these methods has no real way of finding out if the
+ * class really will send the events, or if it just defines the methods to
+ * be able to implement an interface.
+ * </p>
+ *
+ * @since 6.2
+ * @see BlurListener
+ * @see BlurEvent
+ */
+ public interface BlurNotifier extends Serializable {
+ /**
+ * Adds a <code>BlurListener</code> to the Component which gets fired
+ * when a <code>Field</code> loses keyboard focus.
+ *
+ * @param listener
+ * @see BlurListener
+ * @since 6.2
+ */
+ public void addListener(BlurListener listener);
+
+ /**
+ * Removes a <code>BlurListener</code> from the Component.
+ *
+ * @param listener
+ * @see BlurListener
+ * @since 6.2
+ */
+ public void removeListener(BlurListener listener);
+ }
+
+ /**
+ * <code>FocusEvent</code> class for holding additional event information.
+ * Fired when a <code>Field</code> receives keyboard focus.
+ *
+ * @since 6.2
+ */
+ @SuppressWarnings("serial")
+ public class FocusEvent extends Component.Event {
+
+ /**
+ * Identifier for event that can be used in {@link EventRouter}
+ */
+ public static final String EVENT_ID = EventId.FOCUS;
+
+ public FocusEvent(Component source) {
+ super(source);
+ }
+ }
+
+ /**
+ * <code>FocusListener</code> interface for listening for
+ * <code>FocusEvent</code> fired by a <code>Field</code>.
+ *
+ * @see FocusEvent
+ * @since 6.2
+ */
+ public interface FocusListener extends ComponentEventListener {
+
+ public static final Method focusMethod = ReflectTools.findMethod(
+ FocusListener.class, "focus", FocusEvent.class);
+
+ /**
+ * Component has been focused
+ *
+ * @param event
+ * Component focus event.
+ */
+ public void focus(FocusEvent event);
+ }
+
+ /**
+ * <code>BlurEvent</code> class for holding additional event information.
+ * Fired when a <code>Field</code> loses keyboard focus.
+ *
+ * @since 6.2
+ */
+ @SuppressWarnings("serial")
+ public class BlurEvent extends Component.Event {
+
+ /**
+ * Identifier for event that can be used in {@link EventRouter}
+ */
+ public static final String EVENT_ID = EventId.BLUR;
+
+ public BlurEvent(Component source) {
+ super(source);
+ }
+ }
+
+ /**
+ * <code>BlurListener</code> interface for listening for
+ * <code>BlurEvent</code> fired by a <code>Field</code>.
+ *
+ * @see BlurEvent
+ * @since 6.2
+ */
+ public interface BlurListener extends ComponentEventListener {
+
+ public static final Method blurMethod = ReflectTools.findMethod(
+ BlurListener.class, "blur", BlurEvent.class);
+
+ /**
+ * Component has been blurred
+ *
+ * @param event
+ * Component blur event.
+ */
+ public void blur(BlurEvent event);
+ }
+
+ /**
+ * TextChangeEvents are fired when the user is editing the text content of a
+ * field. Most commonly text change events are triggered by typing text with
+ * keyboard, but e.g. pasting content from clip board to a text field also
+ * triggers an event.
+ * <p>
+ * TextChangeEvents differ from {@link ValueChangeEvent}s so that they are
+ * triggered repeatedly while the end user is filling the field.
+ * ValueChangeEvents are not fired until the user for example hits enter or
+ * focuses another field. Also note the difference that TextChangeEvents are
+ * only fired if the change is triggered from the user, while
+ * ValueChangeEvents are also fired if the field value is set by the
+ * application code.
+ * <p>
+ * The {@link TextChangeNotifier}s implementation may decide when exactly
+ * TextChangeEvents are fired. TextChangeEvents are not necessary fire for
+ * example on each key press, but buffered with a small delay. The
+ * {@link TextField} component supports different modes for triggering
+ * TextChangeEvents.
+ *
+ * @see TextChangeListener
+ * @see TextChangeNotifier
+ * @see TextField#setTextChangeEventMode(com.vaadin.ui.TextField.TextChangeEventMode)
+ * @since 6.5
+ */
+ public static abstract class TextChangeEvent extends Component.Event {
+ public TextChangeEvent(Component source) {
+ super(source);
+ }
+
+ /**
+ * @return the text content of the field after the
+ * {@link TextChangeEvent}
+ */
+ public abstract String getText();
+
+ /**
+ * @return the cursor position during after the {@link TextChangeEvent}
+ */
+ public abstract int getCursorPosition();
+ }
+
+ /**
+ * A listener for {@link TextChangeEvent}s.
+ *
+ * @since 6.5
+ */
+ public interface TextChangeListener extends ComponentEventListener {
+
+ public static String EVENT_ID = "ie";
+ public static Method EVENT_METHOD = ReflectTools.findMethod(
+ TextChangeListener.class, "textChange", TextChangeEvent.class);
+
+ /**
+ * This method is called repeatedly while the text is edited by a user.
+ *
+ * @param event
+ * the event providing details of the text change
+ */
+ public void textChange(TextChangeEvent event);
+ }
+
+ /**
+ * An interface implemented by a {@link Field} supporting
+ * {@link TextChangeEvent}s. An example a {@link TextField} supports
+ * {@link TextChangeListener}s.
+ */
+ public interface TextChangeNotifier extends Serializable {
+ public void addListener(TextChangeListener listener);
+
+ public void removeListener(TextChangeListener listener);
+ }
+
+ public static abstract class FocusAndBlurServerRpcImpl implements
+ FocusAndBlurServerRpc {
+
+ private Component component;
+
+ public FocusAndBlurServerRpcImpl(Component component) {
+ this.component = component;
+ }
+
+ protected abstract void fireEvent(Event event);
+
+ @Override
+ public void blur() {
+ fireEvent(new BlurEvent(component));
+ }
+
+ @Override
+ public void focus() {
+ fireEvent(new FocusEvent(component));
+ }
+ };
+
+}
diff --git a/server/src/com/vaadin/event/ItemClickEvent.java b/server/src/com/vaadin/event/ItemClickEvent.java
new file mode 100644
index 0000000000..c736353614
--- /dev/null
+++ b/server/src/com/vaadin/event/ItemClickEvent.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.event.MouseEvents.ClickEvent;
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.ui.Component;
+
+/**
+ *
+ * Click event fired by a {@link Component} implementing
+ * {@link com.vaadin.data.Container} interface. ItemClickEvents happens on an
+ * {@link Item} rendered somehow on terminal. Event may also contain a specific
+ * {@link Property} on which the click event happened.
+ *
+ * @since 5.3
+ *
+ */
+@SuppressWarnings("serial")
+public class ItemClickEvent extends ClickEvent implements Serializable {
+ private Item item;
+ private Object itemId;
+ private Object propertyId;
+
+ public ItemClickEvent(Component source, Item item, Object itemId,
+ Object propertyId, MouseEventDetails details) {
+ super(source, details);
+ this.item = item;
+ this.itemId = itemId;
+ this.propertyId = propertyId;
+ }
+
+ /**
+ * Gets the item on which the click event occurred.
+ *
+ * @return item which was clicked
+ */
+ public Item getItem() {
+ return item;
+ }
+
+ /**
+ * Gets a possible identifier in source for clicked Item
+ *
+ * @return
+ */
+ public Object getItemId() {
+ return itemId;
+ }
+
+ /**
+ * Returns property on which click event occurred. Returns null if source
+ * cannot be resolved at property leve. For example if clicked a cell in
+ * table, the "column id" is returned.
+ *
+ * @return a property id of clicked property or null if click didn't occur
+ * on any distinct property.
+ */
+ public Object getPropertyId() {
+ return propertyId;
+ }
+
+ public static final Method ITEM_CLICK_METHOD;
+
+ static {
+ try {
+ ITEM_CLICK_METHOD = ItemClickListener.class.getDeclaredMethod(
+ "itemClick", new Class[] { ItemClickEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException();
+ }
+ }
+
+ public interface ItemClickListener extends Serializable {
+ public void itemClick(ItemClickEvent event);
+ }
+
+ /**
+ * The interface for adding and removing <code>ItemClickEvent</code>
+ * listeners. By implementing this interface a class explicitly announces
+ * that it will generate an <code>ItemClickEvent</code> when one of its
+ * items is clicked.
+ * <p>
+ * Note: The general Java convention is not to explicitly declare that a
+ * class generates events, but to directly define the
+ * <code>addListener</code> and <code>removeListener</code> methods. That
+ * way the caller of these methods has no real way of finding out if the
+ * class really will send the events, or if it just defines the methods to
+ * be able to implement an interface.
+ * </p>
+ *
+ * @since 6.5
+ * @see ItemClickListener
+ * @see ItemClickEvent
+ */
+ public interface ItemClickNotifier extends Serializable {
+ /**
+ * Register a listener to handle {@link ItemClickEvent}s.
+ *
+ * @param listener
+ * ItemClickListener to be registered
+ */
+ public void addListener(ItemClickListener listener);
+
+ /**
+ * Removes an ItemClickListener.
+ *
+ * @param listener
+ * ItemClickListener to be removed
+ */
+ public void removeListener(ItemClickListener listener);
+ }
+
+}
diff --git a/server/src/com/vaadin/event/LayoutEvents.java b/server/src/com/vaadin/event/LayoutEvents.java
new file mode 100644
index 0000000000..7fc6e5a42d
--- /dev/null
+++ b/server/src/com/vaadin/event/LayoutEvents.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+import com.vaadin.event.MouseEvents.ClickEvent;
+import com.vaadin.shared.Connector;
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.ComponentContainer;
+
+public interface LayoutEvents {
+
+ public interface LayoutClickListener extends ComponentEventListener {
+
+ public static final Method clickMethod = ReflectTools.findMethod(
+ LayoutClickListener.class, "layoutClick",
+ LayoutClickEvent.class);
+
+ /**
+ * Layout has been clicked
+ *
+ * @param event
+ * Component click event.
+ */
+ public void layoutClick(LayoutClickEvent event);
+ }
+
+ /**
+ * The interface for adding and removing <code>LayoutClickEvent</code>
+ * listeners. By implementing this interface a class explicitly announces
+ * that it will generate a <code>LayoutClickEvent</code> when a component
+ * inside it is clicked and a <code>LayoutClickListener</code> is
+ * registered.
+ * <p>
+ * Note: The general Java convention is not to explicitly declare that a
+ * class generates events, but to directly define the
+ * <code>addListener</code> and <code>removeListener</code> methods. That
+ * way the caller of these methods has no real way of finding out if the
+ * class really will send the events, or if it just defines the methods to
+ * be able to implement an interface.
+ * </p>
+ *
+ * @since 6.5.2
+ * @see LayoutClickListener
+ * @see LayoutClickEvent
+ */
+ public interface LayoutClickNotifier extends Serializable {
+ /**
+ * Add a click listener to the layout. The listener is called whenever
+ * the user clicks inside the layout. An event is also triggered when
+ * the click targets a component inside a nested layout or Panel,
+ * provided the targeted component does not prevent the click event from
+ * propagating. A caption is not considered part of a component.
+ *
+ * The child component that was clicked is included in the
+ * {@link LayoutClickEvent}.
+ *
+ * Use {@link #removeListener(LayoutClickListener)} to remove the
+ * listener.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public void addListener(LayoutClickListener listener);
+
+ /**
+ * Removes an LayoutClickListener.
+ *
+ * @param listener
+ * LayoutClickListener to be removed
+ */
+ public void removeListener(LayoutClickListener listener);
+ }
+
+ /**
+ * An event fired when the layout has been clicked. The event contains
+ * information about the target layout (component) and the child component
+ * that was clicked. If no child component was found it is set to null.
+ */
+ public static class LayoutClickEvent extends ClickEvent {
+
+ private final Component clickedComponent;
+ private final Component childComponent;
+
+ public LayoutClickEvent(Component source,
+ MouseEventDetails mouseEventDetails,
+ Component clickedComponent, Component childComponent) {
+ super(source, mouseEventDetails);
+ this.clickedComponent = clickedComponent;
+ this.childComponent = childComponent;
+ }
+
+ /**
+ * Returns the component that was clicked, which is somewhere inside the
+ * parent layout on which the listener was registered.
+ *
+ * For the direct child component of the layout, see
+ * {@link #getChildComponent()}.
+ *
+ * @return clicked {@link Component}, null if none found
+ */
+ public Component getClickedComponent() {
+ return clickedComponent;
+ }
+
+ /**
+ * Returns the direct child component of the layout which contains the
+ * clicked component.
+ *
+ * For the clicked component inside that child component of the layout,
+ * see {@link #getClickedComponent()}.
+ *
+ * @return direct child {@link Component} of the layout which contains
+ * the clicked Component, null if none found
+ */
+ public Component getChildComponent() {
+ return childComponent;
+ }
+
+ public static LayoutClickEvent createEvent(ComponentContainer layout,
+ MouseEventDetails mouseDetails, Connector clickedConnector) {
+ Component clickedComponent = (Component) clickedConnector;
+ Component childComponent = clickedComponent;
+ while (childComponent != null
+ && childComponent.getParent() != layout) {
+ childComponent = childComponent.getParent();
+ }
+
+ return new LayoutClickEvent(layout, mouseDetails, clickedComponent,
+ childComponent);
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/ListenerMethod.java b/server/src/com/vaadin/event/ListenerMethod.java
new file mode 100644
index 0000000000..2c43b31390
--- /dev/null
+++ b/server/src/com/vaadin/event/ListenerMethod.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>
+ * One registered event listener. This class contains the listener object
+ * reference, listened event type, the trigger method to call when the event
+ * fires, and the optional argument list to pass to the method and the index of
+ * the argument to replace with the event object.
+ * </p>
+ *
+ * <p>
+ * This Class provides several constructors that allow omission of the optional
+ * arguments, and giving the listener method directly, or having the constructor
+ * to reflect it using merely the name of the method.
+ * </p>
+ *
+ * <p>
+ * It should be pointed out that the method
+ * {@link #receiveEvent(EventObject event)} is the one that filters out the
+ * events that do not match with the given event type and thus do not result in
+ * calling of the trigger method.
+ * </p>
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+@SuppressWarnings("serial")
+public class ListenerMethod implements EventListener, Serializable {
+
+ /**
+ * Type of the event that should trigger this listener. Also the subclasses
+ * of this class are accepted to trigger the listener.
+ */
+ private final Class<?> eventType;
+
+ /**
+ * The object containing the trigger method.
+ */
+ private final Object target;
+
+ /**
+ * The trigger method to call when an event passing the given criteria
+ * fires.
+ */
+ private transient Method method;
+
+ /**
+ * Optional argument set to pass to the trigger method.
+ */
+ private Object[] arguments;
+
+ /**
+ * Optional index to <code>arguments</code> that point out which one should
+ * be replaced with the triggering event object and thus be passed to the
+ * trigger method.
+ */
+ private int eventArgumentIndex;
+
+ /* Special serialization to handle method references */
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ try {
+ out.defaultWriteObject();
+ String name = method.getName();
+ Class<?>[] paramTypes = method.getParameterTypes();
+ out.writeObject(name);
+ out.writeObject(paramTypes);
+ } catch (NotSerializableException e) {
+ getLogger().warning(
+ "Error in serialization of the application: Class "
+ + target.getClass().getName()
+ + " must implement serialization.");
+ throw e;
+ }
+
+ };
+
+ /* Special serialization to handle method references */
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ try {
+ String name = (String) in.readObject();
+ Class<?>[] paramTypes = (Class<?>[]) in.readObject();
+ // We can not use getMethod directly as we want to support anonymous
+ // inner classes
+ method = findHighestMethod(target.getClass(), name, paramTypes);
+ } catch (SecurityException e) {
+ getLogger().log(Level.SEVERE, "Internal deserialization error", e);
+ }
+ };
+
+ private static Method findHighestMethod(Class<?> cls, String method,
+ Class<?>[] paramTypes) {
+ Class<?>[] ifaces = cls.getInterfaces();
+ for (int i = 0; i < ifaces.length; i++) {
+ Method ifaceMethod = findHighestMethod(ifaces[i], method,
+ paramTypes);
+ if (ifaceMethod != null) {
+ return ifaceMethod;
+ }
+ }
+ if (cls.getSuperclass() != null) {
+ Method parentMethod = findHighestMethod(cls.getSuperclass(),
+ method, paramTypes);
+ if (parentMethod != null) {
+ return parentMethod;
+ }
+ }
+ Method[] methods = cls.getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ // we ignore parameter types for now - you need to add this
+ if (methods[i].getName().equals(method)) {
+ return methods[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <p>
+ * Constructs a new event listener from a trigger method, it's arguments and
+ * the argument index specifying which one is replaced with the event object
+ * when the trigger method is called.
+ * </p>
+ *
+ * <p>
+ * This constructor gets the trigger method as a parameter so it does not
+ * need to reflect to find it out.
+ * </p>
+ *
+ * @param eventType
+ * the event type that is listener listens to. All events of this
+ * kind (or its subclasses) result in calling the trigger method.
+ * @param target
+ * the object instance that contains the trigger method
+ * @param method
+ * the trigger method
+ * @param arguments
+ * the arguments to be passed to the trigger method
+ * @param eventArgumentIndex
+ * An index to the argument list. This index points out the
+ * argument that is replaced with the event object before the
+ * argument set is passed to the trigger method. If the
+ * eventArgumentIndex is negative, the triggering event object
+ * will not be passed to the trigger method, though it is still
+ * called.
+ * @throws java.lang.IllegalArgumentException
+ * if <code>method</code> is not a member of <code>target</code>
+ * .
+ */
+ public ListenerMethod(Class<?> eventType, Object target, Method method,
+ Object[] arguments, int eventArgumentIndex)
+ throws java.lang.IllegalArgumentException {
+
+ // Checks that the object is of correct type
+ if (!method.getDeclaringClass().isAssignableFrom(target.getClass())) {
+ throw new java.lang.IllegalArgumentException("The method "
+ + method.getName()
+ + " cannot be used for the given target: "
+ + target.getClass().getName());
+ }
+
+ // Checks that the event argument is null
+ if (eventArgumentIndex >= 0 && arguments[eventArgumentIndex] != null) {
+ throw new java.lang.IllegalArgumentException("argument["
+ + eventArgumentIndex + "] must be null");
+ }
+
+ // Checks the event type is supported by the method
+ if (eventArgumentIndex >= 0
+ && !method.getParameterTypes()[eventArgumentIndex]
+ .isAssignableFrom(eventType)) {
+ throw new java.lang.IllegalArgumentException("The method "
+ + method.getName()
+ + " does not accept the given eventType: "
+ + eventType.getName());
+ }
+
+ this.eventType = eventType;
+ this.target = target;
+ this.method = method;
+ this.arguments = arguments;
+ this.eventArgumentIndex = eventArgumentIndex;
+ }
+
+ /**
+ * <p>
+ * Constructs a new event listener from a trigger method name, it's
+ * arguments and the argument index specifying which one is replaced with
+ * the event object. The actual trigger method is reflected from
+ * <code>object</code>, and <code>java.lang.IllegalArgumentException</code>
+ * is thrown unless exactly one match is found.
+ * </p>
+ *
+ * @param eventType
+ * the event type that is listener listens to. All events of this
+ * kind (or its subclasses) result in calling the trigger method.
+ * @param target
+ * the object instance that contains the trigger method.
+ * @param methodName
+ * the name of the trigger method. If the object does not contain
+ * the method or it contains more than one matching methods
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * @param arguments
+ * the arguments to be passed to the trigger method.
+ * @param eventArgumentIndex
+ * An index to the argument list. This index points out the
+ * argument that is replaced with the event object before the
+ * argument set is passed to the trigger method. If the
+ * eventArgumentIndex is negative, the triggering event object
+ * will not be passed to the trigger method, though it is still
+ * called.
+ * @throws java.lang.IllegalArgumentException
+ * unless exactly one match <code>methodName</code> is found in
+ * <code>target</code>.
+ */
+ public ListenerMethod(Class<?> eventType, Object target, String methodName,
+ Object[] arguments, int eventArgumentIndex)
+ throws java.lang.IllegalArgumentException {
+
+ // Finds the correct method
+ final Method[] methods = target.getClass().getMethods();
+ Method method = null;
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals(methodName)) {
+ method = methods[i];
+ }
+ }
+ if (method == null) {
+ throw new IllegalArgumentException("Method " + methodName
+ + " not found in class " + target.getClass().getName());
+ }
+
+ // Checks that the event argument is null
+ if (eventArgumentIndex >= 0 && arguments[eventArgumentIndex] != null) {
+ throw new java.lang.IllegalArgumentException("argument["
+ + eventArgumentIndex + "] must be null");
+ }
+
+ // Checks the event type is supported by the method
+ if (eventArgumentIndex >= 0
+ && !method.getParameterTypes()[eventArgumentIndex]
+ .isAssignableFrom(eventType)) {
+ throw new java.lang.IllegalArgumentException("The method "
+ + method.getName()
+ + " does not accept the given eventType: "
+ + eventType.getName());
+ }
+
+ this.eventType = eventType;
+ this.target = target;
+ this.method = method;
+ this.arguments = arguments;
+ this.eventArgumentIndex = eventArgumentIndex;
+ }
+
+ /**
+ * <p>
+ * Constructs a new event listener from the trigger method and it's
+ * arguments. Since the the index to the replaced parameter is not specified
+ * the event triggering this listener will not be passed to the trigger
+ * method.
+ * </p>
+ *
+ * <p>
+ * This constructor gets the trigger method as a parameter so it does not
+ * need to reflect to find it out.
+ * </p>
+ *
+ * @param eventType
+ * the event type that is listener listens to. All events of this
+ * kind (or its subclasses) result in calling the trigger method.
+ * @param target
+ * the object instance that contains the trigger method.
+ * @param method
+ * the trigger method.
+ * @param arguments
+ * the arguments to be passed to the trigger method.
+ * @throws java.lang.IllegalArgumentException
+ * if <code>method</code> is not a member of <code>target</code>
+ * .
+ */
+ public ListenerMethod(Class<?> eventType, Object target, Method method,
+ Object[] arguments) throws java.lang.IllegalArgumentException {
+
+ // Check that the object is of correct type
+ if (!method.getDeclaringClass().isAssignableFrom(target.getClass())) {
+ throw new java.lang.IllegalArgumentException("The method "
+ + method.getName()
+ + " cannot be used for the given target: "
+ + target.getClass().getName());
+ }
+
+ this.eventType = eventType;
+ this.target = target;
+ this.method = method;
+ this.arguments = arguments;
+ eventArgumentIndex = -1;
+ }
+
+ /**
+ * <p>
+ * Constructs a new event listener from a trigger method name and it's
+ * arguments. Since the the index to the replaced parameter is not specified
+ * the event triggering this listener will not be passed to the trigger
+ * method.
+ * </p>
+ *
+ * <p>
+ * The actual trigger method is reflected from <code>target</code>, and
+ * <code>java.lang.IllegalArgumentException</code> is thrown unless exactly
+ * one match is found.
+ * </p>
+ *
+ * @param eventType
+ * the event type that is listener listens to. All events of this
+ * kind (or its subclasses) result in calling the trigger method.
+ * @param target
+ * the object instance that contains the trigger method.
+ * @param methodName
+ * the name of the trigger method. If the object does not contain
+ * the method or it contains more than one matching methods
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * @param arguments
+ * the arguments to be passed to the trigger method.
+ * @throws java.lang.IllegalArgumentException
+ * unless exactly one match <code>methodName</code> is found in
+ * <code>object</code>.
+ */
+ public ListenerMethod(Class<?> eventType, Object target, String methodName,
+ Object[] arguments) throws java.lang.IllegalArgumentException {
+
+ // Find the correct method
+ final Method[] methods = target.getClass().getMethods();
+ Method method = null;
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals(methodName)) {
+ method = methods[i];
+ }
+ }
+ if (method == null) {
+ throw new IllegalArgumentException("Method " + methodName
+ + " not found in class " + target.getClass().getName());
+ }
+
+ this.eventType = eventType;
+ this.target = target;
+ this.method = method;
+ this.arguments = arguments;
+ eventArgumentIndex = -1;
+ }
+
+ /**
+ * <p>
+ * Constructs a new event listener from a trigger method. Since the argument
+ * list is unspecified no parameters are passed to the trigger method when
+ * the listener is triggered.
+ * </p>
+ *
+ * <p>
+ * This constructor gets the trigger method as a parameter so it does not
+ * need to reflect to find it out.
+ * </p>
+ *
+ * @param eventType
+ * the event type that is listener listens to. All events of this
+ * kind (or its subclasses) result in calling the trigger method.
+ * @param target
+ * the object instance that contains the trigger method.
+ * @param method
+ * the trigger method.
+ * @throws java.lang.IllegalArgumentException
+ * if <code>method</code> is not a member of <code>object</code>
+ * .
+ */
+ public ListenerMethod(Class<?> eventType, Object target, Method method)
+ throws java.lang.IllegalArgumentException {
+
+ // Checks that the object is of correct type
+ if (!method.getDeclaringClass().isAssignableFrom(target.getClass())) {
+ throw new java.lang.IllegalArgumentException("The method "
+ + method.getName()
+ + " cannot be used for the given target: "
+ + target.getClass().getName());
+ }
+
+ this.eventType = eventType;
+ this.target = target;
+ this.method = method;
+ eventArgumentIndex = -1;
+
+ final Class<?>[] params = method.getParameterTypes();
+
+ if (params.length == 0) {
+ arguments = new Object[0];
+ } else if (params.length == 1 && params[0].isAssignableFrom(eventType)) {
+ arguments = new Object[] { null };
+ eventArgumentIndex = 0;
+ } else {
+ throw new IllegalArgumentException(
+ "Method requires unknown parameters");
+ }
+ }
+
+ /**
+ * <p>
+ * Constructs a new event listener from a trigger method name. Since the
+ * argument list is unspecified no parameters are passed to the trigger
+ * method when the listener is triggered.
+ * </p>
+ *
+ * <p>
+ * The actual trigger method is reflected from <code>object</code>, and
+ * <code>java.lang.IllegalArgumentException</code> is thrown unless exactly
+ * one match is found.
+ * </p>
+ *
+ * @param eventType
+ * the event type that is listener listens to. All events of this
+ * kind (or its subclasses) result in calling the trigger method.
+ * @param target
+ * the object instance that contains the trigger method.
+ * @param methodName
+ * the name of the trigger method. If the object does not contain
+ * the method or it contains more than one matching methods
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * @throws java.lang.IllegalArgumentException
+ * unless exactly one match <code>methodName</code> is found in
+ * <code>target</code>.
+ */
+ public ListenerMethod(Class<?> eventType, Object target, String methodName)
+ throws java.lang.IllegalArgumentException {
+
+ // Finds the correct method
+ final Method[] methods = target.getClass().getMethods();
+ Method method = null;
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals(methodName)) {
+ method = methods[i];
+ }
+ }
+ if (method == null) {
+ throw new IllegalArgumentException("Method " + methodName
+ + " not found in class " + target.getClass().getName());
+ }
+
+ this.eventType = eventType;
+ this.target = target;
+ this.method = method;
+ eventArgumentIndex = -1;
+
+ final Class<?>[] params = method.getParameterTypes();
+
+ if (params.length == 0) {
+ arguments = new Object[0];
+ } else if (params.length == 1 && params[0].isAssignableFrom(eventType)) {
+ arguments = new Object[] { null };
+ eventArgumentIndex = 0;
+ } else {
+ throw new IllegalArgumentException(
+ "Method requires unknown parameters");
+ }
+ }
+
+ /**
+ * Receives one event from the <code>EventRouter</code> and calls the
+ * trigger method if it matches with the criteria defined for the listener.
+ * Only the events of the same or subclass of the specified event class
+ * result in the trigger method to be called.
+ *
+ * @param event
+ * the fired event. Unless the trigger method's argument list and
+ * the index to the to be replaced argument is specified, this
+ * event will not be passed to the trigger method.
+ */
+ public void receiveEvent(EventObject event) {
+ // Only send events supported by the method
+ if (eventType.isAssignableFrom(event.getClass())) {
+ try {
+ if (eventArgumentIndex >= 0) {
+ if (eventArgumentIndex == 0 && arguments.length == 1) {
+ method.invoke(target, new Object[] { event });
+ } else {
+ final Object[] arg = new Object[arguments.length];
+ for (int i = 0; i < arg.length; i++) {
+ arg[i] = arguments[i];
+ }
+ arg[eventArgumentIndex] = event;
+ method.invoke(target, arg);
+ }
+ } else {
+ method.invoke(target, arguments);
+ }
+
+ } catch (final java.lang.IllegalAccessException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException(
+ "Internal error - please report", e);
+ } catch (final java.lang.reflect.InvocationTargetException e) {
+ // An exception was thrown by the invocation target. Throw it
+ // forwards.
+ throw new MethodException("Invocation of method "
+ + method.getName() + " in "
+ + target.getClass().getName() + " failed.",
+ e.getTargetException());
+ }
+ }
+ }
+
+ /**
+ * Checks if the given object and event match with the ones stored in this
+ * listener.
+ *
+ * @param target
+ * the object to be matched against the object stored by this
+ * listener.
+ * @param eventType
+ * the type to be tested for equality against the type stored by
+ * this listener.
+ * @return <code>true</code> if <code>target</code> is the same object as
+ * the one stored in this object and <code>eventType</code> equals
+ * the event type stored in this object. *
+ */
+ public boolean matches(Class<?> eventType, Object target) {
+ return (this.target == target) && (eventType.equals(this.eventType));
+ }
+
+ /**
+ * Checks if the given object, event and method match with the ones stored
+ * in this listener.
+ *
+ * @param target
+ * the object to be matched against the object stored by this
+ * listener.
+ * @param eventType
+ * the type to be tested for equality against the type stored by
+ * this listener.
+ * @param method
+ * the method to be tested for equality against the method stored
+ * by this listener.
+ * @return <code>true</code> if <code>target</code> is the same object as
+ * the one stored in this object, <code>eventType</code> equals with
+ * the event type stored in this object and <code>method</code>
+ * equals with the method stored in this object
+ */
+ public boolean matches(Class<?> eventType, Object target, Method method) {
+ return (this.target == target)
+ && (eventType.equals(this.eventType) && method
+ .equals(this.method));
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+
+ hash = 31 * hash + eventArgumentIndex;
+ hash = 31 * hash + (eventType == null ? 0 : eventType.hashCode());
+ hash = 31 * hash + (target == null ? 0 : target.hashCode());
+ hash = 31 * hash + (method == null ? 0 : method.hashCode());
+
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ // return false if obj is a subclass (do not use instanceof check)
+ if ((obj == null) || (obj.getClass() != getClass())) {
+ return false;
+ }
+
+ // obj is of same class, test it further
+ ListenerMethod t = (ListenerMethod) obj;
+
+ return eventArgumentIndex == t.eventArgumentIndex
+ && (eventType == t.eventType || (eventType != null && eventType
+ .equals(t.eventType)))
+ && (target == t.target || (target != null && target
+ .equals(t.target)))
+ && (method == t.method || (method != null && method
+ .equals(t.method)))
+ && (arguments == t.arguments || (Arrays.equals(arguments,
+ t.arguments)));
+ }
+
+ /**
+ * Exception that wraps an exception thrown by an invoked method. When
+ * <code>ListenerMethod</code> invokes the target method, it may throw
+ * arbitrary exception. The original exception is wrapped into
+ * MethodException instance and rethrown by the <code>ListenerMethod</code>.
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+ public class MethodException extends RuntimeException implements
+ Serializable {
+
+ private MethodException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ }
+
+ /**
+ * Compares the type of this ListenerMethod to the given type
+ *
+ * @param eventType
+ * The type to compare with
+ * @return true if this type of this ListenerMethod matches the given type,
+ * false otherwise
+ */
+ public boolean isType(Class<?> eventType) {
+ return this.eventType == eventType;
+ }
+
+ /**
+ * Compares the type of this ListenerMethod to the given type
+ *
+ * @param eventType
+ * The type to compare with
+ * @return true if this event type can be assigned to the given type, false
+ * otherwise
+ */
+ public boolean isOrExtendsType(Class<?> eventType) {
+ return eventType.isAssignableFrom(this.eventType);
+ }
+
+ /**
+ * Returns the target object which contains the trigger method.
+ *
+ * @return The target object
+ */
+ public Object getTarget() {
+ return target;
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(ListenerMethod.class.getName());
+ }
+
+}
diff --git a/server/src/com/vaadin/event/MethodEventSource.java b/server/src/com/vaadin/event/MethodEventSource.java
new file mode 100644
index 0000000000..5ebdc2bb99
--- /dev/null
+++ b/server/src/com/vaadin/event/MethodEventSource.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+/**
+ * <p>
+ * Interface for classes supporting registration of methods as event receivers.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+public interface MethodEventSource extends Serializable {
+
+ /**
+ * <p>
+ * Registers a new event listener with the specified activation method to
+ * listen events generated by this component. If the activation method does
+ * not have any arguments the event object will not be passed to it when
+ * it's called.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the type of the listened event. Events of this type or its
+ * subclasses activate the listener.
+ * @param object
+ * the object instance who owns the activation method.
+ * @param method
+ * the activation method.
+ * @throws java.lang.IllegalArgumentException
+ * unless <code>method</code> has exactly one match in
+ * <code>object</code>
+ */
+ public void addListener(Class<?> eventType, Object object, Method method);
+
+ /**
+ * <p>
+ * Registers a new listener with the specified activation method to listen
+ * events generated by this component. If the activation method does not
+ * have any arguments the event object will not be passed to it when it's
+ * called.
+ * </p>
+ *
+ * <p>
+ * This version of <code>addListener</code> gets the name of the activation
+ * method as a parameter. The actual method is reflected from
+ * <code>object</code>, and unless exactly one match is found,
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the type of the listened event. Events of this type or its
+ * subclasses activate the listener.
+ * @param object
+ * the object instance who owns the activation method.
+ * @param methodName
+ * the name of the activation method.
+ * @throws java.lang.IllegalArgumentException
+ * unless <code>method</code> has exactly one match in
+ * <code>object</code>
+ */
+ public void addListener(Class<?> eventType, Object object, String methodName);
+
+ /**
+ * Removes all registered listeners matching the given parameters. Since
+ * this method receives the event type and the listener object as
+ * parameters, it will unregister all <code>object</code>'s methods that are
+ * registered to listen to events of type <code>eventType</code> generated
+ * by this component.
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * the target object that has registered to listen to events of
+ * type <code>eventType</code> with one or more methods.
+ */
+ public void removeListener(Class<?> eventType, Object target);
+
+ /**
+ * Removes one registered listener method. The given method owned by the
+ * given object will no longer be called when the specified events are
+ * generated by this component.
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * the target object that has registered to listen to events of
+ * type eventType with one or more methods.
+ * @param method
+ * the method owned by the target that's registered to listen to
+ * events of type eventType.
+ */
+ public void removeListener(Class<?> eventType, Object target, Method method);
+
+ /**
+ * <p>
+ * Removes one registered listener method. The given method owned by the
+ * given object will no longer be called when the specified events are
+ * generated by this component.
+ * </p>
+ *
+ * <p>
+ * This version of <code>removeListener</code> gets the name of the
+ * activation method as a parameter. The actual method is reflected from the
+ * target, and unless exactly one match is found,
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * the target object that has registered to listen to events of
+ * type <code>eventType</code> with one or more methods.
+ * @param methodName
+ * the name of the method owned by <code>target</code> that's
+ * registered to listen to events of type <code>eventType</code>.
+ */
+ public void removeListener(Class<?> eventType, Object target,
+ String methodName);
+}
diff --git a/server/src/com/vaadin/event/MouseEvents.java b/server/src/com/vaadin/event/MouseEvents.java
new file mode 100644
index 0000000000..0fe9902b2e
--- /dev/null
+++ b/server/src/com/vaadin/event/MouseEvents.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.lang.reflect.Method;
+
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.tools.ReflectTools;
+import com.vaadin.ui.Component;
+
+/**
+ * Interface that serves as a wrapper for mouse related events.
+ *
+ * @author Vaadin Ltd.
+ * @see ClickListener
+ * @since 6.2
+ */
+public interface MouseEvents {
+
+ /**
+ * Class for holding information about a mouse click event. A
+ * {@link ClickEvent} is fired when the user clicks on a
+ * <code>Component</code>.
+ *
+ * The information available for click events are terminal dependent.
+ * Correct values for all event details cannot be guaranteed.
+ *
+ * @author Vaadin Ltd.
+ * @see ClickListener
+ * @since 6.2
+ */
+ public class ClickEvent extends Component.Event {
+ public static final int BUTTON_LEFT = MouseEventDetails.BUTTON_LEFT;
+ public static final int BUTTON_MIDDLE = MouseEventDetails.BUTTON_MIDDLE;
+ public static final int BUTTON_RIGHT = MouseEventDetails.BUTTON_RIGHT;
+
+ private MouseEventDetails details;
+
+ public ClickEvent(Component source, MouseEventDetails mouseEventDetails) {
+ super(source);
+ details = mouseEventDetails;
+ }
+
+ /**
+ * Returns an identifier describing which mouse button the user pushed.
+ * Compare with {@link #BUTTON_LEFT},{@link #BUTTON_MIDDLE},
+ * {@link #BUTTON_RIGHT} to find out which butten it is.
+ *
+ * @return one of {@link #BUTTON_LEFT}, {@link #BUTTON_MIDDLE},
+ * {@link #BUTTON_RIGHT}.
+ */
+ public int getButton() {
+ return details.getButton();
+ }
+
+ /**
+ * Returns the mouse position (x coordinate) when the click took place.
+ * The position is relative to the browser client area.
+ *
+ * @return The mouse cursor x position
+ */
+ public int getClientX() {
+ return details.getClientX();
+ }
+
+ /**
+ * Returns the mouse position (y coordinate) when the click took place.
+ * The position is relative to the browser client area.
+ *
+ * @return The mouse cursor y position
+ */
+ public int getClientY() {
+ return details.getClientY();
+ }
+
+ /**
+ * Returns the relative mouse position (x coordinate) when the click
+ * took place. The position is relative to the clicked component.
+ *
+ * @return The mouse cursor x position relative to the clicked layout
+ * component or -1 if no x coordinate available
+ */
+ public int getRelativeX() {
+ return details.getRelativeX();
+ }
+
+ /**
+ * Returns the relative mouse position (y coordinate) when the click
+ * took place. The position is relative to the clicked component.
+ *
+ * @return The mouse cursor y position relative to the clicked layout
+ * component or -1 if no y coordinate available
+ */
+ public int getRelativeY() {
+ return details.getRelativeY();
+ }
+
+ /**
+ * Checks if the event is a double click event.
+ *
+ * @return true if the event is a double click event, false otherwise
+ */
+ public boolean isDoubleClick() {
+ return details.isDoubleClick();
+ }
+
+ /**
+ * Checks if the Alt key was down when the mouse event took place.
+ *
+ * @return true if Alt was down when the event occured, false otherwise
+ */
+ public boolean isAltKey() {
+ return details.isAltKey();
+ }
+
+ /**
+ * Checks if the Ctrl key was down when the mouse event took place.
+ *
+ * @return true if Ctrl was pressed when the event occured, false
+ * otherwise
+ */
+ public boolean isCtrlKey() {
+ return details.isCtrlKey();
+ }
+
+ /**
+ * Checks if the Meta key was down when the mouse event took place.
+ *
+ * @return true if Meta was pressed when the event occured, false
+ * otherwise
+ */
+ public boolean isMetaKey() {
+ return details.isMetaKey();
+ }
+
+ /**
+ * Checks if the Shift key was down when the mouse event took place.
+ *
+ * @return true if Shift was pressed when the event occured, false
+ * otherwise
+ */
+ public boolean isShiftKey() {
+ return details.isShiftKey();
+ }
+
+ /**
+ * Returns a human readable string representing which button has been
+ * pushed. This is meant for debug purposes only and the string returned
+ * could change. Use {@link #getButton()} to check which button was
+ * pressed.
+ *
+ * @since 6.3
+ * @return A string representation of which button was pushed.
+ */
+ public String getButtonName() {
+ return details.getButtonName();
+ }
+ }
+
+ /**
+ * Interface for listening for a {@link ClickEvent} fired by a
+ * {@link Component}.
+ *
+ * @see ClickEvent
+ * @author Vaadin Ltd.
+ * @since 6.2
+ */
+ public interface ClickListener extends ComponentEventListener {
+
+ public static final Method clickMethod = ReflectTools.findMethod(
+ ClickListener.class, "click", ClickEvent.class);
+
+ /**
+ * Called when a {@link Component} has been clicked. A reference to the
+ * component is given by {@link ClickEvent#getComponent()}.
+ *
+ * @param event
+ * An event containing information about the click.
+ */
+ public void click(ClickEvent event);
+ }
+
+ /**
+ * Class for holding additional event information for DoubleClick events.
+ * Fired when the user double-clicks on a <code>Component</code>.
+ *
+ * @see ClickEvent
+ * @author Vaadin Ltd.
+ * @since 6.2
+ */
+ public class DoubleClickEvent extends Component.Event {
+
+ public DoubleClickEvent(Component source) {
+ super(source);
+ }
+ }
+
+ /**
+ * Interface for listening for a {@link DoubleClickEvent} fired by a
+ * {@link Component}.
+ *
+ * @see DoubleClickEvent
+ * @author Vaadin Ltd.
+ * @since 6.2
+ */
+ public interface DoubleClickListener extends ComponentEventListener {
+
+ public static final Method doubleClickMethod = ReflectTools.findMethod(
+ DoubleClickListener.class, "doubleClick",
+ DoubleClickEvent.class);
+
+ /**
+ * Called when a {@link Component} has been double clicked. A reference
+ * to the component is given by {@link DoubleClickEvent#getComponent()}.
+ *
+ * @param event
+ * An event containing information about the double click.
+ */
+ public void doubleClick(DoubleClickEvent event);
+ }
+
+}
diff --git a/server/src/com/vaadin/event/ShortcutAction.java b/server/src/com/vaadin/event/ShortcutAction.java
new file mode 100644
index 0000000000..b3f41b474d
--- /dev/null
+++ b/server/src/com/vaadin/event/ShortcutAction.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.vaadin.terminal.Resource;
+import com.vaadin.ui.ComponentContainer;
+import com.vaadin.ui.Panel;
+import com.vaadin.ui.Window;
+
+/**
+ * Shortcuts are a special type of {@link Action}s used to create keyboard
+ * shortcuts.
+ * <p>
+ * The ShortcutAction is triggered when the user presses a given key in
+ * combination with the (optional) given modifier keys.
+ * </p>
+ * <p>
+ * ShortcutActions can be global (by attaching to the {@link Window}), or
+ * attached to different parts of the UI so that a specific shortcut is only
+ * valid in part of the UI. For instance, one can attach shortcuts to a specific
+ * {@link Panel} - look for {@link ComponentContainer}s implementing
+ * {@link Handler Action.Handler} or {@link Notifier Action.Notifier}.
+ * </p>
+ * <p>
+ * ShortcutActions have a caption that may be used to display the shortcut
+ * visually. This allows the ShortcutAction to be used as a plain Action while
+ * still reacting to a keyboard shortcut. Note that this functionality is not
+ * very well supported yet, but it might still be a good idea to give a caption
+ * to the shortcut.
+ * </p>
+ *
+ * @author Vaadin Ltd.
+ * @since 4.0.1
+ */
+@SuppressWarnings("serial")
+public class ShortcutAction extends Action {
+
+ private final int keyCode;
+
+ private final int[] modifiers;
+
+ /**
+ * Creates a shortcut that reacts to the given {@link KeyCode} and
+ * (optionally) {@link ModifierKey}s. <br/>
+ * The shortcut might be shown in the UI (e.g context menu), in which case
+ * the caption will be used.
+ *
+ * @param caption
+ * used when displaying the shortcut visually
+ * @param kc
+ * KeyCode that the shortcut reacts to
+ * @param m
+ * optional modifier keys
+ */
+ public ShortcutAction(String caption, int kc, int[] m) {
+ super(caption);
+ keyCode = kc;
+ modifiers = m;
+ }
+
+ /**
+ * Creates a shortcut that reacts to the given {@link KeyCode} and
+ * (optionally) {@link ModifierKey}s. <br/>
+ * The shortcut might be shown in the UI (e.g context menu), in which case
+ * the caption and icon will be used.
+ *
+ * @param caption
+ * used when displaying the shortcut visually
+ * @param icon
+ * used when displaying the shortcut visually
+ * @param kc
+ * KeyCode that the shortcut reacts to
+ * @param m
+ * optional modifier keys
+ */
+ public ShortcutAction(String caption, Resource icon, int kc, int[] m) {
+ super(caption, icon);
+ keyCode = kc;
+ modifiers = m;
+ }
+
+ /**
+ * Used in the caption shorthand notation to indicate the ALT modifier.
+ */
+ public static final char SHORTHAND_CHAR_ALT = '&';
+ /**
+ * Used in the caption shorthand notation to indicate the SHIFT modifier.
+ */
+ public static final char SHORTHAND_CHAR_SHIFT = '_';
+ /**
+ * Used in the caption shorthand notation to indicate the CTRL modifier.
+ */
+ public static final char SHORTHAND_CHAR_CTRL = '^';
+
+ // regex-quote (escape) the characters
+ private static final String SHORTHAND_ALT = Pattern.quote(Character
+ .toString(SHORTHAND_CHAR_ALT));
+ private static final String SHORTHAND_SHIFT = Pattern.quote(Character
+ .toString(SHORTHAND_CHAR_SHIFT));
+ private static final String SHORTHAND_CTRL = Pattern.quote(Character
+ .toString(SHORTHAND_CHAR_CTRL));
+ // Used for replacing escaped chars, e.g && with &
+ private static final Pattern SHORTHAND_ESCAPE = Pattern.compile("("
+ + SHORTHAND_ALT + "?)" + SHORTHAND_ALT + "|(" + SHORTHAND_SHIFT
+ + "?)" + SHORTHAND_SHIFT + "|(" + SHORTHAND_CTRL + "?)"
+ + SHORTHAND_CTRL);
+ // Used for removing escaped chars, only leaving real shorthands
+ private static final Pattern SHORTHAND_REMOVE = Pattern.compile("(["
+ + SHORTHAND_ALT + "|" + SHORTHAND_SHIFT + "|" + SHORTHAND_CTRL
+ + "])\\1");
+ // Mnemonic char, optionally followed by another, and optionally a third
+ private static final Pattern SHORTHANDS = Pattern.compile("("
+ + SHORTHAND_ALT + "|" + SHORTHAND_SHIFT + "|" + SHORTHAND_CTRL
+ + ")(?!\\1)(?:(" + SHORTHAND_ALT + "|" + SHORTHAND_SHIFT + "|"
+ + SHORTHAND_CTRL + ")(?!\\1|\\2))?(?:(" + SHORTHAND_ALT + "|"
+ + SHORTHAND_SHIFT + "|" + SHORTHAND_CTRL + ")(?!\\1|\\2|\\3))?.");
+
+ /**
+ * Constructs a ShortcutAction using a shorthand notation to encode the
+ * keycode and modifiers in the caption.
+ * <p>
+ * Insert one or more modifier characters before the character to use as
+ * keycode. E.g <code>"&Save"</code> will make a shortcut responding to
+ * ALT-S, <code>"E^xit"</code> will respond to CTRL-X.<br/>
+ * Multiple modifiers can be used, e.g <code>"&^Delete"</code> will respond
+ * to CTRL-ALT-D (the order of the modifier characters is not important).
+ * </p>
+ * <p>
+ * The modifier characters will be removed from the caption. The modifier
+ * character is be escaped by itself: two consecutive characters are turned
+ * into the original character w/o the special meaning. E.g
+ * <code>"Save&&&close"</code> will respond to ALT-C, and the caption will
+ * say "Save&close".
+ * </p>
+ *
+ * @param shorthandCaption
+ * the caption in modifier shorthand
+ */
+ public ShortcutAction(String shorthandCaption) {
+ this(shorthandCaption, null);
+ }
+
+ /**
+ * Constructs a ShortcutAction using a shorthand notation to encode the
+ * keycode a in the caption.
+ * <p>
+ * This works the same way as {@link #ShortcutAction(String)}, with the
+ * exception that the modifiers given override those indicated in the
+ * caption. I.e use any of the modifier characters in the caption to
+ * indicate the keycode, but the modifier will be the given set.<br/>
+ * E.g
+ * <code>new ShortcutAction("Do &stuff", new int[]{ShortcutAction.ModifierKey.CTRL}));</code>
+ * will respond to CTRL-S.
+ * </p>
+ *
+ * @param shorthandCaption
+ * @param modifierKeys
+ */
+ public ShortcutAction(String shorthandCaption, int[] modifierKeys) {
+ // && -> & etc
+ super(SHORTHAND_ESCAPE.matcher(shorthandCaption).replaceAll("$1$2$3"));
+ // replace escaped chars with something that won't accidentally match
+ shorthandCaption = SHORTHAND_REMOVE.matcher(shorthandCaption)
+ .replaceAll("\u001A");
+ Matcher matcher = SHORTHANDS.matcher(shorthandCaption);
+ if (matcher.find()) {
+ String match = matcher.group();
+
+ // KeyCode from last char in match, uppercase
+ keyCode = Character.toUpperCase(matcher.group().charAt(
+ match.length() - 1));
+
+ // Given modifiers override this indicated in the caption
+ if (modifierKeys != null) {
+ modifiers = modifierKeys;
+ } else {
+ // Read modifiers from caption
+ int[] mod = new int[match.length() - 1];
+ for (int i = 0; i < mod.length; i++) {
+ int kc = match.charAt(i);
+ switch (kc) {
+ case SHORTHAND_CHAR_ALT:
+ mod[i] = ModifierKey.ALT;
+ break;
+ case SHORTHAND_CHAR_CTRL:
+ mod[i] = ModifierKey.CTRL;
+ break;
+ case SHORTHAND_CHAR_SHIFT:
+ mod[i] = ModifierKey.SHIFT;
+ break;
+ }
+ }
+ modifiers = mod;
+ }
+
+ } else {
+ keyCode = -1;
+ modifiers = modifierKeys;
+ }
+ }
+
+ /**
+ * Get the {@link KeyCode} that this shortcut reacts to (in combination with
+ * the {@link ModifierKey}s).
+ *
+ * @return keycode for this shortcut
+ */
+ public int getKeyCode() {
+ return keyCode;
+ }
+
+ /**
+ * Get the {@link ModifierKey}s required for the shortcut to react.
+ *
+ * @return modifier keys for this shortcut
+ */
+ public int[] getModifiers() {
+ return modifiers;
+ }
+
+ /**
+ * Key codes that can be used for shortcuts
+ *
+ */
+ public interface KeyCode extends Serializable {
+ public static final int ENTER = 13;
+
+ public static final int ESCAPE = 27;
+
+ public static final int PAGE_UP = 33;
+
+ public static final int PAGE_DOWN = 34;
+
+ public static final int TAB = 9;
+
+ public static final int ARROW_LEFT = 37;
+
+ public static final int ARROW_UP = 38;
+
+ public static final int ARROW_RIGHT = 39;
+
+ public static final int ARROW_DOWN = 40;
+
+ public static final int BACKSPACE = 8;
+
+ public static final int DELETE = 46;
+
+ public static final int INSERT = 45;
+
+ public static final int END = 35;
+
+ public static final int HOME = 36;
+
+ public static final int F1 = 112;
+
+ public static final int F2 = 113;
+
+ public static final int F3 = 114;
+
+ public static final int F4 = 115;
+
+ public static final int F5 = 116;
+
+ public static final int F6 = 117;
+
+ public static final int F7 = 118;
+
+ public static final int F8 = 119;
+
+ public static final int F9 = 120;
+
+ public static final int F10 = 121;
+
+ public static final int F11 = 122;
+
+ public static final int F12 = 123;
+
+ public static final int A = 65;
+
+ public static final int B = 66;
+
+ public static final int C = 67;
+
+ public static final int D = 68;
+
+ public static final int E = 69;
+
+ public static final int F = 70;
+
+ public static final int G = 71;
+
+ public static final int H = 72;
+
+ public static final int I = 73;
+
+ public static final int J = 74;
+
+ public static final int K = 75;
+
+ public static final int L = 76;
+
+ public static final int M = 77;
+
+ public static final int N = 78;
+
+ public static final int O = 79;
+
+ public static final int P = 80;
+
+ public static final int Q = 81;
+
+ public static final int R = 82;
+
+ public static final int S = 83;
+
+ public static final int T = 84;
+
+ public static final int U = 85;
+
+ public static final int V = 86;
+
+ public static final int W = 87;
+
+ public static final int X = 88;
+
+ public static final int Y = 89;
+
+ public static final int Z = 90;
+
+ public static final int NUM0 = 48;
+
+ public static final int NUM1 = 49;
+
+ public static final int NUM2 = 50;
+
+ public static final int NUM3 = 51;
+
+ public static final int NUM4 = 52;
+
+ public static final int NUM5 = 53;
+
+ public static final int NUM6 = 54;
+
+ public static final int NUM7 = 55;
+
+ public static final int NUM8 = 56;
+
+ public static final int NUM9 = 57;
+
+ public static final int SPACEBAR = 32;
+ }
+
+ /**
+ * Modifier key constants
+ *
+ */
+ public interface ModifierKey extends Serializable {
+ public static final int SHIFT = 16;
+
+ public static final int CTRL = 17;
+
+ public static final int ALT = 18;
+
+ public static final int META = 91;
+ }
+}
diff --git a/server/src/com/vaadin/event/ShortcutListener.java b/server/src/com/vaadin/event/ShortcutListener.java
new file mode 100644
index 0000000000..82a09585b3
--- /dev/null
+++ b/server/src/com/vaadin/event/ShortcutListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import com.vaadin.event.Action.Listener;
+import com.vaadin.terminal.Resource;
+
+public abstract class ShortcutListener extends ShortcutAction implements
+ Listener {
+
+ private static final long serialVersionUID = 1L;
+
+ public ShortcutListener(String caption, int keyCode, int... modifierKeys) {
+ super(caption, keyCode, modifierKeys);
+ }
+
+ public ShortcutListener(String shorthandCaption, int... modifierKeys) {
+ super(shorthandCaption, modifierKeys);
+ }
+
+ public ShortcutListener(String caption, Resource icon, int keyCode,
+ int... modifierKeys) {
+ super(caption, icon, keyCode, modifierKeys);
+ }
+
+ public ShortcutListener(String shorthandCaption) {
+ super(shorthandCaption);
+ }
+
+ @Override
+ abstract public void handleAction(Object sender, Object target);
+}
diff --git a/server/src/com/vaadin/event/Transferable.java b/server/src/com/vaadin/event/Transferable.java
new file mode 100644
index 0000000000..8b614b6b37
--- /dev/null
+++ b/server/src/com/vaadin/event/Transferable.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import com.vaadin.ui.Component;
+
+/**
+ * Transferable wraps the data that is to be imported into another component.
+ * Currently Transferable is only used for drag and drop.
+ *
+ * @since 6.3
+ */
+public interface Transferable extends Serializable {
+
+ /**
+ * Returns the data from Transferable by its data flavor (aka data type).
+ * Data types can be any string keys, but MIME types like "text/plain" are
+ * commonly used.
+ * <p>
+ * Note, implementations of {@link Transferable} often provide a better
+ * typed API for accessing data.
+ *
+ * @param dataFlavor
+ * the data flavor to be returned from Transferable
+ * @return the data stored in the Transferable or null if Transferable
+ * contains no data for given data flavour
+ */
+ public Object getData(String dataFlavor);
+
+ /**
+ * Stores data of given data flavor to Transferable. Possibly existing value
+ * of the same data flavor will be replaced.
+ *
+ * @param dataFlavor
+ * the data flavor
+ * @param value
+ * the new value of the data flavor
+ */
+ public void setData(String dataFlavor, Object value);
+
+ /**
+ * @return a collection of data flavors ( data types ) available in this
+ * Transferable
+ */
+ public Collection<String> getDataFlavors();
+
+ /**
+ * @return the component that created the Transferable or null if the source
+ * component is unknown
+ */
+ public Component getSourceComponent();
+
+}
diff --git a/server/src/com/vaadin/event/TransferableImpl.java b/server/src/com/vaadin/event/TransferableImpl.java
new file mode 100644
index 0000000000..388916d8fa
--- /dev/null
+++ b/server/src/com/vaadin/event/TransferableImpl.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.ui.Component;
+
+/**
+ * TODO Javadoc!
+ *
+ * @since 6.3
+ */
+public class TransferableImpl implements Transferable {
+ private Map<String, Object> rawVariables = new HashMap<String, Object>();
+ private Component sourceComponent;
+
+ public TransferableImpl(Component sourceComponent,
+ Map<String, Object> rawVariables) {
+ this.sourceComponent = sourceComponent;
+ this.rawVariables = rawVariables;
+ }
+
+ @Override
+ public Component getSourceComponent() {
+ return sourceComponent;
+ }
+
+ @Override
+ public Object getData(String dataFlavor) {
+ return rawVariables.get(dataFlavor);
+ }
+
+ @Override
+ public void setData(String dataFlavor, Object value) {
+ rawVariables.put(dataFlavor, value);
+ }
+
+ @Override
+ public Collection<String> getDataFlavors() {
+ return rawVariables.keySet();
+ }
+
+}
diff --git a/server/src/com/vaadin/event/dd/DragAndDropEvent.java b/server/src/com/vaadin/event/dd/DragAndDropEvent.java
new file mode 100644
index 0000000000..d7d2b24f94
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/DragAndDropEvent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd;
+
+import java.io.Serializable;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
+
+/**
+ * DragAndDropEvent wraps information related to drag and drop operation. It is
+ * passed by terminal implementation for
+ * {@link DropHandler#drop(DragAndDropEvent)} and
+ * {@link AcceptCriterion#accept(DragAndDropEvent)} methods.
+ * <p>
+ * DragAndDropEvent instances contains both the dragged data in
+ * {@link Transferable} (generated by {@link DragSource} and details about the
+ * current drop event in {@link TargetDetails} (generated by {@link DropTarget}.
+ *
+ * @since 6.3
+ *
+ */
+public class DragAndDropEvent implements Serializable {
+ private Transferable transferable;
+ private TargetDetails dropTargetDetails;
+
+ public DragAndDropEvent(Transferable transferable,
+ TargetDetails dropTargetDetails) {
+ this.transferable = transferable;
+ this.dropTargetDetails = dropTargetDetails;
+ }
+
+ /**
+ * @return the Transferable instance representing the data dragged in this
+ * drag and drop event
+ */
+ public Transferable getTransferable() {
+ return transferable;
+ }
+
+ /**
+ * @return the TargetDetails containing drop target related details of drag
+ * and drop operation
+ */
+ public TargetDetails getTargetDetails() {
+ return dropTargetDetails;
+ }
+
+}
diff --git a/server/src/com/vaadin/event/dd/DragSource.java b/server/src/com/vaadin/event/dd/DragSource.java
new file mode 100644
index 0000000000..f42fd8b61b
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/DragSource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd;
+
+import java.util.Map;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Tree;
+
+/**
+ * DragSource is a {@link Component} that builds a {@link Transferable} for a
+ * drag and drop operation.
+ * <p>
+ * In Vaadin the drag and drop operation practically starts from client side
+ * component. The client side component initially defines the data that will be
+ * present in {@link Transferable} object on server side. If the server side
+ * counterpart of the component implements this interface, terminal
+ * implementation lets it create the {@link Transferable} instance from the raw
+ * client side "seed data". This way server side implementation may translate or
+ * extend the data that will be available for {@link DropHandler}.
+ *
+ * @since 6.3
+ *
+ */
+public interface DragSource extends Component {
+
+ /**
+ * DragSource may convert data added by client side component to meaningful
+ * values for server side developer or add other data based on it.
+ *
+ * <p>
+ * For example Tree converts item identifiers to generated string keys for
+ * the client side. Vaadin developer don't and can't know anything about
+ * these generated keys, only about item identifiers. When tree node is
+ * dragged client puts that key to {@link Transferable}s client side
+ * counterpart. In {@link Tree#getTransferable(Map)} the key is converted
+ * back to item identifier that the server side developer can use.
+ * <p>
+ *
+ * @since 6.3
+ * @param rawVariables
+ * the data that client side initially included in
+ * {@link Transferable}s client side counterpart.
+ * @return the {@link Transferable} instance that will be passed to
+ * {@link DropHandler} (and/or {@link AcceptCriterion})
+ */
+ public Transferable getTransferable(Map<String, Object> rawVariables);
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/DropHandler.java b/server/src/com/vaadin/event/dd/DropHandler.java
new file mode 100644
index 0000000000..36d25e5da1
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/DropHandler.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd;
+
+import java.io.Serializable;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.dd.acceptcriteria.AcceptAll;
+import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
+import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
+
+/**
+ * DropHandlers contain the actual business logic for drag and drop operations.
+ * <p>
+ * The {@link #drop(DragAndDropEvent)} method is used to receive the transferred
+ * data and the {@link #getAcceptCriterion()} method contains the (possibly
+ * client side verifiable) criterion whether the dragged data will be handled at
+ * all.
+ *
+ * @since 6.3
+ *
+ */
+public interface DropHandler extends Serializable {
+
+ /**
+ * Drop method is called when the end user has finished the drag operation
+ * on a {@link DropTarget} and {@link DragAndDropEvent} has passed
+ * {@link AcceptCriterion} defined by {@link #getAcceptCriterion()} method.
+ * The actual business logic of drag and drop operation is implemented into
+ * this method.
+ *
+ * @param event
+ * the event related to this drop
+ */
+ public void drop(DragAndDropEvent event);
+
+ /**
+ * Returns the {@link AcceptCriterion} used to evaluate whether the
+ * {@link Transferable} will be handed over to
+ * {@link DropHandler#drop(DragAndDropEvent)} method. If client side can't
+ * verify the {@link AcceptCriterion}, the same criteria may be tested also
+ * prior to actual drop - during the drag operation.
+ * <p>
+ * Based on information from {@link AcceptCriterion} components may display
+ * some hints for the end user whether the drop will be accepted or not.
+ * <p>
+ * Vaadin contains a variety of criteria built in that can be composed to
+ * more complex criterion. If the build in criteria are not enough,
+ * developer can use a {@link ServerSideCriterion} or build own custom
+ * criterion with client side counterpart.
+ * <p>
+ * If developer wants to handle everything in the
+ * {@link #drop(DragAndDropEvent)} method, {@link AcceptAll} instance can be
+ * returned.
+ *
+ * @return the {@link AcceptCriterion}
+ */
+ public AcceptCriterion getAcceptCriterion();
+
+}
diff --git a/server/src/com/vaadin/event/dd/DropTarget.java b/server/src/com/vaadin/event/dd/DropTarget.java
new file mode 100644
index 0000000000..9a4ef05cae
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/DropTarget.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd;
+
+import java.util.Map;
+
+import com.vaadin.ui.Component;
+
+/**
+ * DropTarget is an interface for components supporting drop operations. A
+ * component that wants to receive drop events should implement this interface
+ * and provide a {@link DropHandler} which will handle the actual drop event.
+ *
+ * @since 6.3
+ */
+public interface DropTarget extends Component {
+
+ /**
+ * @return the drop hanler that will receive the dragged data or null if
+ * drops are not currently accepted
+ */
+ public DropHandler getDropHandler();
+
+ /**
+ * Called before the {@link DragAndDropEvent} is passed to
+ * {@link DropHandler}. Implementation may for example translate the drop
+ * target details provided by the client side (drop target) to meaningful
+ * server side values. If null is returned the terminal implementation will
+ * automatically create a {@link TargetDetails} with raw client side data.
+ *
+ * @see DragSource#getTransferable(Map)
+ *
+ * @param clientVariables
+ * data passed from the DropTargets client side counterpart.
+ * @return A DropTargetDetails object with the translated data or null to
+ * use a default implementation.
+ */
+ public TargetDetails translateDropTargetDetails(
+ Map<String, Object> clientVariables);
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/TargetDetails.java b/server/src/com/vaadin/event/dd/TargetDetails.java
new file mode 100644
index 0000000000..eb67b49090
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/TargetDetails.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd;
+
+import java.io.Serializable;
+
+import com.vaadin.ui.Tree.TreeTargetDetails;
+
+/**
+ * TargetDetails wraps drop target related information about
+ * {@link DragAndDropEvent}.
+ * <p>
+ * When a TargetDetails object is used in {@link DropHandler} it is often
+ * preferable to cast the TargetDetails to an implementation provided by
+ * DropTarget like {@link TreeTargetDetails}. They often provide a better typed,
+ * drop target specific API.
+ *
+ * @since 6.3
+ *
+ */
+public interface TargetDetails extends Serializable {
+
+ /**
+ * Gets target data associated with the given string key
+ *
+ * @param key
+ * @return The data associated with the key
+ */
+ public Object getData(String key);
+
+ /**
+ * @return the drop target on which the {@link DragAndDropEvent} happened.
+ */
+ public DropTarget getTarget();
+
+}
diff --git a/server/src/com/vaadin/event/dd/TargetDetailsImpl.java b/server/src/com/vaadin/event/dd/TargetDetailsImpl.java
new file mode 100644
index 0000000000..7c0c98bb79
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/TargetDetailsImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A HashMap backed implementation of {@link TargetDetails} for terminal
+ * implementation and for extension.
+ *
+ * @since 6.3
+ *
+ */
+@SuppressWarnings("serial")
+public class TargetDetailsImpl implements TargetDetails {
+
+ private HashMap<String, Object> data = new HashMap<String, Object>();
+ private DropTarget dropTarget;
+
+ protected TargetDetailsImpl(Map<String, Object> rawDropData) {
+ data.putAll(rawDropData);
+ }
+
+ public TargetDetailsImpl(Map<String, Object> rawDropData,
+ DropTarget dropTarget) {
+ this(rawDropData);
+ this.dropTarget = dropTarget;
+ }
+
+ @Override
+ public Object getData(String key) {
+ return data.get(key);
+ }
+
+ public Object setData(String key, Object value) {
+ return data.put(key, value);
+ }
+
+ @Override
+ public DropTarget getTarget() {
+ return dropTarget;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java b/server/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java
new file mode 100644
index 0000000000..a8ef49c21c
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/AcceptAll.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.dd.DragAndDropEvent;
+
+/**
+ * Criterion that accepts all drops anywhere on the component.
+ * <p>
+ * Note! Class is singleton, use {@link #get()} method to get the instance.
+ *
+ *
+ * @since 6.3
+ *
+ */
+public final class AcceptAll extends ClientSideCriterion {
+
+ private static final long serialVersionUID = 7406683402153141461L;
+ private static AcceptCriterion singleton = new AcceptAll();
+
+ private AcceptAll() {
+ }
+
+ public static AcceptCriterion get() {
+ return singleton;
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/AcceptCriterion.java b/server/src/com/vaadin/event/dd/acceptcriteria/AcceptCriterion.java
new file mode 100644
index 0000000000..7b04efc4b3
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/AcceptCriterion.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import java.io.Serializable;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * Criterion that can be used create policy to accept/discard dragged content
+ * (presented by {@link Transferable}).
+ *
+ * The drag and drop mechanism will verify the criteria returned by
+ * {@link DropHandler#getAcceptCriterion()} before calling
+ * {@link DropHandler#drop(DragAndDropEvent)}.
+ *
+ * The criteria can be evaluated either on the client (browser - see
+ * {@link ClientSideCriterion}) or on the server (see
+ * {@link ServerSideCriterion}). If no constraints are needed, an
+ * {@link AcceptAll} can be used.
+ *
+ * In addition to accepting or rejecting a possible drop, criteria can provide
+ * additional hints for client side painting.
+ *
+ * @see DropHandler
+ * @see ClientSideCriterion
+ * @see ServerSideCriterion
+ *
+ * @since 6.3
+ */
+public interface AcceptCriterion extends Serializable {
+
+ /**
+ * Returns whether the criteria can be checked on the client or whether a
+ * server request is needed to check the criteria.
+ *
+ * This requirement may depend on the state of the criterion (e.g. logical
+ * operations between criteria), so this cannot be based on a marker
+ * interface.
+ */
+ public boolean isClientSideVerifiable();
+
+ public void paint(PaintTarget target) throws PaintException;
+
+ /**
+ * This needs to be implemented iff criterion does some lazy server side
+ * initialization. The UIDL painted in this method will be passed to client
+ * side drop handler implementation. Implementation can assume that
+ * {@link #accept(DragAndDropEvent)} is called before this method.
+ *
+ * @param target
+ * @throws PaintException
+ */
+ public void paintResponse(PaintTarget target) throws PaintException;
+
+ /**
+ * Validates the data in event to be appropriate for the
+ * {@link DropHandler#drop(DragAndDropEvent)} method.
+ * <p>
+ * Note that even if your criterion is validated on client side, you should
+ * always validate the data on server side too.
+ *
+ * @param dragEvent
+ * @return
+ */
+ public boolean accept(DragAndDropEvent dragEvent);
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/And.java b/server/src/com/vaadin/event/dd/acceptcriteria/And.java
new file mode 100644
index 0000000000..3d11ecf7bf
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/And.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * A compound criterion that accepts the drag if all of its criteria accepts the
+ * drag.
+ *
+ * @see Or
+ *
+ * @since 6.3
+ *
+ */
+public class And extends ClientSideCriterion {
+
+ private static final long serialVersionUID = -5242574480825471748L;
+ protected ClientSideCriterion[] criteria;
+
+ /**
+ *
+ * @param criteria
+ * criteria of which the And criterion will be composed
+ */
+ public And(ClientSideCriterion... criteria) {
+ this.criteria = criteria;
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ for (ClientSideCriterion crit : criteria) {
+ crit.paint(target);
+ }
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ for (ClientSideCriterion crit : criteria) {
+ if (!crit.accept(dragEvent)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/ClientSideCriterion.java b/server/src/com/vaadin/event/dd/acceptcriteria/ClientSideCriterion.java
new file mode 100644
index 0000000000..be7e2d4033
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/ClientSideCriterion.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import java.io.Serializable;
+
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * Parent class for criteria that can be completely validated on client side.
+ * All classes that provide criteria that can be completely validated on client
+ * side should extend this class.
+ *
+ * It is recommended that subclasses of ClientSideCriterion re-validate the
+ * condition on the server side in
+ * {@link AcceptCriterion#accept(com.vaadin.event.dd.DragAndDropEvent)} after
+ * the client side validation has accepted a transfer.
+ *
+ * @since 6.3
+ */
+public abstract class ClientSideCriterion implements Serializable,
+ AcceptCriterion {
+
+ /*
+ * All criteria that extend this must be completely validatable on client
+ * side.
+ *
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#isClientSideVerifiable
+ * ()
+ */
+ @Override
+ public final boolean isClientSideVerifiable() {
+ return true;
+ }
+
+ @Override
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("-ac");
+ target.addAttribute("name", getIdentifier());
+ paintContent(target);
+ target.endTag("-ac");
+ }
+
+ protected void paintContent(PaintTarget target) throws PaintException {
+ }
+
+ protected String getIdentifier() {
+ return getClass().getCanonicalName();
+ }
+
+ @Override
+ public final void paintResponse(PaintTarget target) throws PaintException {
+ // NOP, nothing to do as this is client side verified criterion
+ }
+
+}
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java b/server/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java
new file mode 100644
index 0000000000..55ee17fea9
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/ContainsDataFlavor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * A Criterion that checks whether {@link Transferable} contains given data
+ * flavor. The developer might for example accept the incoming data only if it
+ * contains "Url" or "Text".
+ *
+ * @since 6.3
+ */
+public class ContainsDataFlavor extends ClientSideCriterion {
+
+ private String dataFlavorId;
+
+ /**
+ * Constructs a new instance of {@link ContainsDataFlavor}.
+ *
+ * @param dataFlawor
+ * the type of data that will be checked from
+ * {@link Transferable}
+ */
+ public ContainsDataFlavor(String dataFlawor) {
+ dataFlavorId = dataFlawor;
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ target.addAttribute("p", dataFlavorId);
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ return dragEvent.getTransferable().getDataFlavors()
+ .contains(dataFlavorId);
+ }
+
+ @Override
+ protected String getIdentifier() {
+ // extending classes use client side implementation from this class
+ return ContainsDataFlavor.class.getCanonicalName();
+ }
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/Not.java b/server/src/com/vaadin/event/dd/acceptcriteria/Not.java
new file mode 100644
index 0000000000..b3f73699ea
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/Not.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * Criterion that wraps another criterion and inverts its return value.
+ *
+ * @since 6.3
+ *
+ */
+public class Not extends ClientSideCriterion {
+
+ private static final long serialVersionUID = 1131422338558613244L;
+ private AcceptCriterion acceptCriterion;
+
+ public Not(ClientSideCriterion acceptCriterion) {
+ this.acceptCriterion = acceptCriterion;
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ acceptCriterion.paint(target);
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ return !acceptCriterion.accept(dragEvent);
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/Or.java b/server/src/com/vaadin/event/dd/acceptcriteria/Or.java
new file mode 100644
index 0000000000..42d1c3293d
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/Or.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * A compound criterion that accepts the drag if any of its criterion accepts
+ * it.
+ *
+ * @see And
+ *
+ * @since 6.3
+ *
+ */
+public class Or extends ClientSideCriterion {
+ private static final long serialVersionUID = 1L;
+ private AcceptCriterion criteria[];
+
+ /**
+ * @param criteria
+ * the criteria of which the Or criteria will be composed
+ */
+ public Or(ClientSideCriterion... criteria) {
+ this.criteria = criteria;
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ for (AcceptCriterion crit : criteria) {
+ crit.paint(target);
+ }
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ for (AcceptCriterion crit : criteria) {
+ if (crit.accept(dragEvent)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java b/server/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java
new file mode 100644
index 0000000000..b9c2855021
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/ServerSideCriterion.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import java.io.Serializable;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * Parent class for criteria which are verified on the server side during a drag
+ * operation to accept/discard dragged content (presented by
+ * {@link Transferable}).
+ * <p>
+ * Subclasses should implement the
+ * {@link AcceptCriterion#accept(com.vaadin.event.dd.DragAndDropEvent)} method.
+ * <p>
+ * As all server side state can be used to make a decision, this is more
+ * flexible than {@link ClientSideCriterion}. However, this does require
+ * additional requests from the browser to the server during a drag operation.
+ *
+ * @see AcceptCriterion
+ * @see ClientSideCriterion
+ *
+ * @since 6.3
+ */
+public abstract class ServerSideCriterion implements Serializable,
+ AcceptCriterion {
+
+ private static final long serialVersionUID = 2128510128911628902L;
+
+ @Override
+ public final boolean isClientSideVerifiable() {
+ return false;
+ }
+
+ @Override
+ public void paint(PaintTarget target) throws PaintException {
+ target.startTag("-ac");
+ target.addAttribute("name", getIdentifier());
+ paintContent(target);
+ target.endTag("-ac");
+ }
+
+ public void paintContent(PaintTarget target) {
+ }
+
+ @Override
+ public void paintResponse(PaintTarget target) throws PaintException {
+ }
+
+ protected String getIdentifier() {
+ return ServerSideCriterion.class.getCanonicalName();
+ }
+}
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
new file mode 100644
index 0000000000..cc1d586076
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.event.TransferableImpl;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.ui.Component;
+
+/**
+ * Client side criteria that checks if the drag source is one of the given
+ * components.
+ *
+ * @since 6.3
+ */
+@SuppressWarnings("serial")
+public class SourceIs extends ClientSideCriterion {
+
+ private Component[] components;
+
+ public SourceIs(Component... component) {
+ components = component;
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ int paintedComponents = 0;
+ for (int i = 0; i < components.length; i++) {
+ Component c = components[i];
+ if (c.getApplication() != null) {
+ target.addAttribute("component" + paintedComponents++, c);
+ } else {
+ Logger.getLogger(SourceIs.class.getName())
+ .log(Level.WARNING,
+ "SourceIs component {0} at index {1} is not attached to the component hierachy and will thus be ignored",
+ new Object[] { c.getClass().getName(),
+ Integer.valueOf(i) });
+ }
+ }
+ target.addAttribute("c", paintedComponents);
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ if (dragEvent.getTransferable() instanceof TransferableImpl) {
+ Component sourceComponent = ((TransferableImpl) dragEvent
+ .getTransferable()).getSourceComponent();
+ for (Component c : components) {
+ if (c == sourceComponent) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java
new file mode 100644
index 0000000000..a4b5f24619
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIsTarget.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.Transferable;
+import com.vaadin.event.TransferableImpl;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.event.dd.DropTarget;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Tree;
+
+/**
+ *
+ * A criterion that ensures the drag source is the same as drop target. Eg.
+ * {@link Tree} or {@link Table} could support only re-ordering of items, but no
+ * {@link Transferable}s coming outside.
+ * <p>
+ * Note! Class is singleton, use {@link #get()} method to get the instance.
+ *
+ * @since 6.3
+ *
+ */
+public class SourceIsTarget extends ClientSideCriterion {
+
+ private static final long serialVersionUID = -451399314705532584L;
+ private static SourceIsTarget instance = new SourceIsTarget();
+
+ private SourceIsTarget() {
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ if (dragEvent.getTransferable() instanceof TransferableImpl) {
+ Component sourceComponent = ((TransferableImpl) dragEvent
+ .getTransferable()).getSourceComponent();
+ DropTarget target = dragEvent.getTargetDetails().getTarget();
+ return sourceComponent == target;
+ }
+ return false;
+ }
+
+ public static synchronized SourceIsTarget get() {
+ return instance;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java b/server/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java
new file mode 100644
index 0000000000..536ba8780e
--- /dev/null
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/TargetDetailIs.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ *
+ * 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.vaadin.event.dd.acceptcriteria;
+
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.event.dd.TargetDetails;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+
+/**
+ * Criterion for checking if drop target details contains the specific property
+ * with the specific value. Currently only String values are supported.
+ *
+ * @since 6.3
+ *
+ * TODO add support for other basic data types that we support in UIDL.
+ *
+ */
+public class TargetDetailIs extends ClientSideCriterion {
+
+ private static final long serialVersionUID = 763165450054331246L;
+ private String propertyName;
+ private Object value;
+
+ /**
+ * Constructs a criterion which ensures that the value there is a value in
+ * {@link TargetDetails} that equals the reference value.
+ *
+ * @param dataFlavor
+ * the type of data to be checked
+ * @param value
+ * the reference value to which the drop target detail will be
+ * compared
+ */
+ public TargetDetailIs(String dataFlavor, String value) {
+ propertyName = dataFlavor;
+ this.value = value;
+ }
+
+ public TargetDetailIs(String dataFlavor, Boolean true1) {
+ propertyName = dataFlavor;
+ value = true1;
+ }
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ super.paintContent(target);
+ target.addAttribute("p", propertyName);
+ if (value instanceof Boolean) {
+ target.addAttribute("v", ((Boolean) value).booleanValue());
+ target.addAttribute("t", "b");
+ } else if (value instanceof String) {
+ target.addAttribute("v", (String) value);
+ }
+ }
+
+ @Override
+ public boolean accept(DragAndDropEvent dragEvent) {
+ Object data = dragEvent.getTargetDetails().getData(propertyName);
+ return value.equals(data);
+ }
+
+ @Override
+ protected String getIdentifier() {
+ // sub classes by default use VDropDetailEquals a client implementation
+ return TargetDetailIs.class.getCanonicalName();
+ }
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/event/package.html b/server/src/com/vaadin/event/package.html
new file mode 100644
index 0000000000..2e7e17b892
--- /dev/null
+++ b/server/src/com/vaadin/event/package.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+
+<!-- Package summary here -->
+
+<p>Provides classes and interfaces for the inheritable event
+model. The model supports inheritable events and a flexible way of
+registering and unregistering event listeners. It's a fundamental building
+block of Vaadin, and as it is included in
+{@link com.vaadin.ui.AbstractComponent}, all UI components
+automatically support it.</p>
+
+<h2>Package Specification</h2>
+
+<p>The core of the event model is the inheritable event class
+hierarchy, and the {@link com.vaadin.event.EventRouter EventRouter}
+which provide a simple, ubiquitous mechanism to transport events to all
+interested parties.</p>
+
+<p>The power of the event inheritance arises from the possibility of
+receiving not only the events of the registered type, <i>but also the
+ones which are inherited from it</i>. For example, let's assume that there
+are the events <code>GeneralEvent</code> and <code>SpecializedEvent</code>
+so that the latter inherits the former. Furthermore we have an object
+<code>A</code> which registers to receive <code>GeneralEvent</code> type
+events from the object <code>B</code>. <code>A</code> would of course
+receive all <code>GeneralEvent</code>s generated by <code>B</code>, but in
+addition to this, <code>A</code> would also receive all
+<code>SpecializedEvent</code>s generated by <code>B</code>. However, if
+<code>B</code> generates some other events that do not have
+<code>GeneralEvent</code> as an ancestor, <code>A</code> would not receive
+them unless it registers to listen for them, too.</p>
+
+<p>The interface to attaching and detaching listeners to and from an object
+works with methods. One specifies the event that should trigger the listener,
+the trigger method that should be called when a suitable event occurs and the
+object owning the method. From these a new listener is constructed and added
+to the event router of the specified component.</p>
+
+<p>The interface is defined in
+{@link com.vaadin.event.MethodEventSource MethodEventSource}, and a
+straightforward implementation of it is defined in
+{@link com.vaadin.event.EventRouter EventRouter} which also includes
+a method to actually fire the events.</p>
+
+<p>All fired events are passed to all registered listeners, which are of
+type {@link com.vaadin.event.ListenerMethod ListenerMethod}. The
+listener then checks if the event type matches with the specified event
+type and calls the specified trigger method if it does.</p>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>