diff options
Diffstat (limited to 'server/src/com/vaadin/event')
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> |