From ab4e63b78dd81ccc99d81d3fc4cb9f9455d5fce6 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Fri, 20 Nov 2009 12:33:35 +0000 Subject: [PATCH] Initial commit for framework enhancement for #3234 svn changeset:9947/svn branch:event-framework-3234 --- src/com/vaadin/event/ClientEventList.java | 82 +++++++ .../vaadin/event/ComponentEventListener.java | 8 + src/com/vaadin/event/FieldEvents.java | 92 +++++++ src/com/vaadin/event/MouseEvents.java | 82 +++++++ .../gwt/client/ApplicationConnection.java | 27 +- .../terminal/gwt/client/ComponentDetail.java | 34 +-- .../gwt/client/ComponentEventHandler.java | 114 +++++++++ .../terminal/gwt/client/ui/VEmbedded.java | 10 +- .../terminal/gwt/client/ui/VGridLayout.java | 31 ++- .../gwt/client/ui/VOrderedLayout.java | 57 ++++- .../vaadin/terminal/gwt/client/ui/VPanel.java | 12 +- .../terminal/gwt/client/ui/VTextField.java | 2 + src/com/vaadin/tools/ReflectTools.java | 29 +++ src/com/vaadin/ui/AbstractComponent.java | 231 ++++++++++++++++-- src/com/vaadin/ui/AbstractOrderedLayout.java | 67 +++++ src/com/vaadin/ui/Component.java | 9 + src/com/vaadin/ui/Embedded.java | 18 ++ src/com/vaadin/ui/GridLayout.java | 34 ++- src/com/vaadin/ui/Panel.java | 21 ++ src/com/vaadin/ui/TextField.java | 33 +++ 20 files changed, 945 insertions(+), 48 deletions(-) create mode 100644 src/com/vaadin/event/ClientEventList.java create mode 100644 src/com/vaadin/event/ComponentEventListener.java create mode 100644 src/com/vaadin/event/FieldEvents.java create mode 100644 src/com/vaadin/event/MouseEvents.java create mode 100644 src/com/vaadin/terminal/gwt/client/ComponentEventHandler.java create mode 100644 src/com/vaadin/tools/ReflectTools.java diff --git a/src/com/vaadin/event/ClientEventList.java b/src/com/vaadin/event/ClientEventList.java new file mode 100644 index 0000000000..633026ca8b --- /dev/null +++ b/src/com/vaadin/event/ClientEventList.java @@ -0,0 +1,82 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.event; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * this class is used to store the registered events so a list of the required + * client event identifiers (that the client-side should listen for and send to + * the server-side) can be sent to the client-side via a variable. + * + * @author davengo GmbH (Germany/Berlin, www.davengo.com) + * + */ +public class ClientEventList { + + /** + * the list containing the registered client events (as strings for + * client-side transfer) + */ + private Map clientEvents = null; + + /** + * initializes the list if necessary + */ + private void checkList() { + if (clientEvents == null) { + clientEvents = new HashMap(); + } + } + + /** + * listens for the event
+ *
+ * increments the internal counter for the event by one + * + * @param eventIdentifier + * the identifier of the event to listen for + */ + public void listenEvent(String eventIdentifier) { + checkList(); + if (!clientEvents.keySet().contains(eventIdentifier)) + clientEvents.put(eventIdentifier, 1); + else + clientEvents.put(eventIdentifier, + clientEvents.get(eventIdentifier) + 1); + } + + /** + * unlistens for an event
+ *
+ * decrements the internal counter by one, if 0 is reached the event is + * removed from the event-list + * + * @param eventIdentifier + * the identifier of the event to stop listening for + */ + public void unlistenEvent(String eventIdentifier) { + checkList(); + if (clientEvents.keySet().contains(eventIdentifier)) { + clientEvents.put(eventIdentifier, + clientEvents.get(eventIdentifier) - 1); + if (clientEvents.get(eventIdentifier) <= 0) + clientEvents.remove(eventIdentifier); + } + } + + /** + * @return a string array containing all registered events + */ + public String[] getEvents() { + if (clientEvents == null) { + return new String[] {}; + } + return clientEvents.keySet().toArray(new String[] {}); + } + +} diff --git a/src/com/vaadin/event/ComponentEventListener.java b/src/com/vaadin/event/ComponentEventListener.java new file mode 100644 index 0000000000..723fc090c0 --- /dev/null +++ b/src/com/vaadin/event/ComponentEventListener.java @@ -0,0 +1,8 @@ +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/src/com/vaadin/event/FieldEvents.java b/src/com/vaadin/event/FieldEvents.java new file mode 100644 index 0000000000..ab81ac85c0 --- /dev/null +++ b/src/com/vaadin/event/FieldEvents.java @@ -0,0 +1,92 @@ +package com.vaadin.event; + +import java.lang.reflect.Method; + +import com.vaadin.tools.ReflectTools; +import com.vaadin.ui.Component; + +public interface FieldEvents { + + /* + * component focus event and listener + */ + + public class FocusEvent extends Component.Event { + + private static final long serialVersionUID = -7644184999481404162L; + + public FocusEvent(Component source) { + super(source); + } + } + + 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); + } + + /* + * component blur event and listener + */ + + public class BlurEvent extends Component.Event { + + private static final long serialVersionUID = -7644184999481404162L; + + public BlurEvent(Component source) { + super(source); + } + } + + 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); + } + + /* + * component value change event + */ + + public class ValueChangeEvent extends Component.Event { + + private static final long serialVersionUID = -7644184999481404162L; + + public ValueChangeEvent(Component source) { + super(source); + } + } + + public interface ValueChangeListener extends ComponentEventListener { + + public static final Method valueChangeMethod = ReflectTools.findMethod( + ValueChangeListener.class, "valueChange", + ValueChangeEvent.class); + + /** + * Component value was changed + * + * @param event + * Component change event. + */ + public void valueChange(ValueChangeEvent event); + } + +} diff --git a/src/com/vaadin/event/MouseEvents.java b/src/com/vaadin/event/MouseEvents.java new file mode 100644 index 0000000000..19fdff8cc4 --- /dev/null +++ b/src/com/vaadin/event/MouseEvents.java @@ -0,0 +1,82 @@ +package com.vaadin.event; + +import java.lang.reflect.Method; + +import com.vaadin.tools.ReflectTools; +import com.vaadin.ui.Component; + +public interface MouseEvents { + + /** + * defines the clicked mouse button for a ComponentClickEvents + */ + public enum MouseButton { + LEFT, RIGHT, MIDDLE + } + + public class ClickEvent extends Component.Event { + + private MouseButton mouseButton; + + private static final long serialVersionUID = -7644184999481404162L; + + public ClickEvent(Component source, String mouseButton) { + super(source); + if (mouseButton.equals("left")) { + this.mouseButton = MouseButton.LEFT; + } else if (mouseButton.equals("right")) { + this.mouseButton = MouseButton.RIGHT; + } else { + this.mouseButton = MouseButton.MIDDLE; + } + } + + public MouseButton getMouseButton() { + return mouseButton; + } + + } + + public interface ClickListener extends ComponentEventListener { + + public static final Method clickMethod = ReflectTools.findMethod( + ClickListener.class, "click", ClickEvent.class); + + /** + * Component has been clicked + * + * @param event + * Component click event. + */ + public void click(ClickEvent event); + } + + /* + * component double click event + */ + + public class DoubleClickEvent extends Component.Event { + + private static final long serialVersionUID = -7644184999481404162L; + + public DoubleClickEvent(Component source) { + super(source); + } + } + + public interface DoubleClickListener extends ComponentEventListener { + + public static final Method doubleClickMethod = ReflectTools.findMethod( + DoubleClickListener.class, "doubleClick", + DoubleClickEvent.class); + + /** + * Component value was changed + * + * @param event + * Component change event. + */ + public void doubleClick(DoubleClickEvent event); + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 4adfc86342..362e3eb8b1 100755 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -845,11 +845,11 @@ public class ApplicationConnection { } }-*/; - public void registerPaintable(String id, Paintable paintable) { - ComponentDetail componentDetail = new ComponentDetail(); - componentDetail.setComponent(paintable); - idToPaintableDetail.put(id, componentDetail); - setPid(((Widget) paintable).getElement(), id); + public void registerPaintable(String pid, Paintable paintable) { + ComponentDetail componentDetail = new ComponentDetail(this, pid, + paintable); + idToPaintableDetail.put(pid, componentDetail); + setPid(((Widget) paintable).getElement(), pid); } private native void setPid(Element el, String pid) @@ -1113,6 +1113,10 @@ public class ApplicationConnection { return true; } + // register the listened events by the server-side to the event-handler + // of the component + componentDetail.getEventHandler().registerEventsFromUIDL(uidl); + // Visibility boolean visible = !uidl.getBooleanAttribute("invisible"); boolean wasVisible = component.isVisible(); @@ -1780,4 +1784,17 @@ public class ApplicationConnection { return configuration; } + /** + * returns the event handler for the given paintable + * + * @param paintable + * @return + */ + public ComponentEventHandler getEventHandler(Paintable paintable) { + ComponentDetail componentDetail = idToPaintableDetail + .get(getPid(paintable)); + + return componentDetail.getEventHandler(); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ComponentDetail.java b/src/com/vaadin/terminal/gwt/client/ComponentDetail.java index 8e61ec7429..8f39733ef1 100644 --- a/src/com/vaadin/terminal/gwt/client/ComponentDetail.java +++ b/src/com/vaadin/terminal/gwt/client/ComponentDetail.java @@ -6,9 +6,21 @@ import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; import com.vaadin.terminal.gwt.client.RenderInformation.Size; class ComponentDetail { - private String pid; + + private ComponentEventHandler eventHandler; private Paintable component; private TooltipInfo tooltipInfo = new TooltipInfo(); + private String pid; + + public ComponentDetail(ApplicationConnection client, String pid, + Paintable component) { + this.component = component; + this.pid = pid; + + // create the event handler for this component + this.eventHandler = new ComponentEventHandler(this, client); + + } /** * Returns a TooltipInfo assosiated with Component. If element is given, @@ -48,14 +60,6 @@ class ComponentDetail { return pid; } - /** - * @param pid - * the pid to set - */ - void setPid(String pid) { - this.pid = pid; - } - /** * @return the component */ @@ -63,14 +67,6 @@ class ComponentDetail { return component; } - /** - * @param component - * the component to set - */ - void setComponent(Paintable component) { - this.component = component; - } - /** * @return the relativeSize */ @@ -112,4 +108,8 @@ class ComponentDetail { } } + public ComponentEventHandler getEventHandler() { + return eventHandler; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ComponentEventHandler.java b/src/com/vaadin/terminal/gwt/client/ComponentEventHandler.java new file mode 100644 index 0000000000..f309f79979 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ComponentEventHandler.java @@ -0,0 +1,114 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * class for event handlers used by ComponentEventHandler + * + * @author davengo GmbH (Germany/Berlin, www.davengo.com) + * + */ +public class ComponentEventHandler { + + public static final String HANDLER_LISTEN_ATTRIBUTE = "listenEvents"; + public static final String HANDLER_TRIGGER_VARIABLE = "fireEvent"; + + private List eventRegistrations; + private ComponentDetail detail; + private ApplicationConnection client; + + public ComponentEventHandler(ComponentDetail detail, + ApplicationConnection client) { + this.detail = detail; + this.client = client; + this.eventRegistrations = null; + } + + /** + * Fires a event which is transmitted to the server and passed on the the + * components handleEvent method provided listeners have been registered on + * the server side. + * + * @param eventIdentifier + * the unique identifier for the event + * @param parameters + * the parameters for the event (can be null) + */ + public void fireEvent(String eventIdentifier, String... parameters) { + fireEvent(eventIdentifier, false, parameters); + } + + /** + * Fires a component event which is transmitted to the server and passed on + * the the components handleEvent method. The event is sent to the server + * even though there are no explicit listeners registered on the server + * side. + * + * @param eventIdentifier + * the unique identifier for the event + * @param parameters + * the parameters for the event (can be null) + */ + public void fireComponentEvent(String eventIdentifier, String... parameters) { + fireEvent(eventIdentifier, true, parameters); + } + + private void fireEvent(String eventIdentifier, boolean forceTransmission, + String... parameters) { + + String[] event; + + // filter events which are not listened on the server-side right here + boolean transmit = forceTransmission + || ((!(eventRegistrations == null)) && eventRegistrations + .contains(eventIdentifier)); + + if (transmit) { + if (parameters != null) { + event = new String[parameters.length + 1]; + event[0] = eventIdentifier; + for (int i = 0; i < parameters.length; i++) { + event[i + 1] = parameters[i]; + } + } else { + event = new String[] { eventIdentifier }; + } + + // transmit the event to the server-side + client.updateVariable(detail.getPid(), HANDLER_TRIGGER_VARIABLE, + event, true); + } + } + + void registerEventsFromUIDL(UIDL componentUIDL) { + + // read out the request event handlers + if (componentUIDL.hasAttribute(HANDLER_LISTEN_ATTRIBUTE)) { + String[] requestedEvents = componentUIDL + .getStringArrayAttribute(HANDLER_LISTEN_ATTRIBUTE); + + // create the eventRegistrations list if necessary + if ((requestedEvents.length > 0) && (eventRegistrations == null)) { + eventRegistrations = new ArrayList(); + } + + // parse the requested event handlers + for (String reqEvent : requestedEvents) { + + if (!eventRegistrations.contains(reqEvent)) { + eventRegistrations.add(reqEvent); + } + + } + + } + + } + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java index c05f1c06d3..159ecafa53 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java @@ -11,6 +11,8 @@ import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.ObjectElement; import com.google.gwt.dom.client.Style; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; @@ -22,7 +24,7 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VTooltip; -public class VEmbedded extends HTML implements Paintable { +public class VEmbedded extends HTML implements Paintable, ClickHandler { private static String CLASSNAME = "v-embedded"; private String height; @@ -33,6 +35,7 @@ public class VEmbedded extends HTML implements Paintable { public VEmbedded() { setStyleName(CLASSNAME); + addClickHandler(this); } public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { @@ -236,4 +239,9 @@ public class VEmbedded extends HTML implements Paintable { client.handleTooltipEvent(event, this); } + + public void onClick(ClickEvent event) { + client.getEventHandler(this).fireEvent("click", "left"); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java index e2f217267a..366476bef0 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java @@ -14,6 +14,9 @@ import java.util.Set; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.SimplePanel; @@ -28,7 +31,8 @@ import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; -public class VGridLayout extends SimplePanel implements Paintable, Container { +public class VGridLayout extends SimplePanel implements Paintable, Container, + ClickHandler { public static final String CLASSNAME = "v-gridlayout"; @@ -71,6 +75,8 @@ public class VGridLayout extends SimplePanel implements Paintable, Container { getElement().appendChild(margin); setStyleName(CLASSNAME); setWidget(canvas); + + addDomHandler(this, ClickEvent.getType()); } @Override @@ -1021,4 +1027,27 @@ public class VGridLayout extends SimplePanel implements Paintable, Container { } return cell; } + + public void onClick(ClickEvent event) { + String col = null; + String row = null; + + Element clickTarget = (Element) event.getNativeEvent().getEventTarget() + .cast(); + Element rootElement = getElement(); + while (clickTarget != null && clickTarget != rootElement) { + Paintable paintable = client.getPaintable(clickTarget); + if (paintable != null) { + Cell cell = paintableToCell.get(paintable); + row = String.valueOf(cell.row); + col = String.valueOf(cell.col); + break; + } + clickTarget = DOM.getParent(clickTarget); + } + + client.getEventHandler(this).fireEvent("click", "left", row, col); + + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java index 282136383d..10d7fe4d02 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java @@ -5,6 +5,12 @@ import java.util.Iterator; import java.util.Set; import com.google.gwt.core.client.JsArrayString; +import com.google.gwt.dom.client.Node; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; @@ -18,7 +24,7 @@ import com.vaadin.terminal.gwt.client.RenderInformation.Size; import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; -public class VOrderedLayout extends CellBasedLayout { +public class VOrderedLayout extends CellBasedLayout implements ClickHandler { public static final String CLASSNAME = "v-orderedlayout"; @@ -49,6 +55,7 @@ public class VOrderedLayout extends CellBasedLayout { public VOrderedLayout() { this(CLASSNAME, ORIENTATION_VERTICAL); allowOrientationUpdate = true; + addDomHandler(this, ClickEvent.getType()); } protected VOrderedLayout(String className, int orientation) { @@ -61,6 +68,7 @@ public class VOrderedLayout extends CellBasedLayout { STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom"; STYLENAME_MARGIN_LEFT = className + "-margin-left"; + addDomHandler(this, ClickEvent.getType()); } @Override @@ -915,4 +923,51 @@ public class VOrderedLayout extends CellBasedLayout { } } + public void onClick(ClickEvent event) { + Integer childComponentId = getChildComponentId((Element) event + .getNativeEvent().getEventTarget().cast()); + String childComponentString = childComponentId.toString(); + client.getEventHandler(this).fireEvent("click", "left", + childComponentString); + + } + + /** + * Returns the index of the child component which contains "element". + * + * @param element + * @return + */ + private int getChildComponentId(Element element) { + Element rootElement = getElement(); + Element parent = DOM.getParent(element); + if (parent == null) { + return -1; + } + Element grandParent = DOM.getParent(parent); + if (grandParent == null) { + return -1; + } + + while (grandParent != null && parent != rootElement) { + if (grandParent == rootElement) { + NodeList nodes = parent.getChildNodes(); + int size = nodes.getLength(); + for (int index = 0; index < size; index++) { + if (nodes.getItem(index) == element) { + return index; + } + } + + // This should not happen + return -1; + } + + element = parent; + parent = grandParent; + grandParent = DOM.getParent(grandParent); + } + return -1; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java index ec4acf2e4d..608a7fd9ab 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java @@ -8,6 +8,8 @@ import java.util.Set; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; @@ -22,7 +24,7 @@ import com.vaadin.terminal.gwt.client.RenderSpace; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; -public class VPanel extends SimplePanel implements Container { +public class VPanel extends SimplePanel implements Container, ClickHandler { public static final String CLASSNAME = "v-panel"; @@ -91,6 +93,9 @@ public class VPanel extends SimplePanel implements Container { DOM.sinkEvents(contentNode, Event.ONSCROLL); contentNode.getStyle().setProperty("position", "relative"); getElement().getStyle().setProperty("overflow", "hidden"); + + addDomHandler(this, ClickEvent.getType()); + } @Override @@ -334,6 +339,8 @@ public class VPanel extends SimplePanel implements Container { @Override public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + final Element target = DOM.eventGetTarget(event); final int type = DOM.eventGetType(event); if (type == Event.ONKEYDOWN && shortcutHandler != null) { @@ -516,4 +523,7 @@ public class VPanel extends SimplePanel implements Container { detectContainerBorders(); } + public void onClick(ClickEvent event) { + client.getEventHandler(this).fireEvent("click", "left"); + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java index 845a50e793..860891f99f 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -195,6 +195,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field, } } focusedTextField = this; + client.getEventHandler(this).fireEvent("focus", (String[]) null); } public void onBlur(BlurEvent event) { @@ -207,6 +208,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field, addStyleDependentName(CLASSNAME_PROMPT); } onChange(null); + client.getEventHandler(this).fireEvent("blur", (String[]) null); } private void setPrompting(boolean prompting) { diff --git a/src/com/vaadin/tools/ReflectTools.java b/src/com/vaadin/tools/ReflectTools.java new file mode 100644 index 0000000000..d74efba105 --- /dev/null +++ b/src/com/vaadin/tools/ReflectTools.java @@ -0,0 +1,29 @@ +package com.vaadin.tools; + +import java.lang.reflect.Method; + +public class ReflectTools { + /** + * Locates the method in the given class. Returns null if the method is not + * found. This method never throws exceptions. Errors in locating methods + * are considered serious problems and are output to standard error. + * + * @param cls + * Class that contains the method + * @param methodName + * The name of the method + * @param parameterTypes + * The parameter types for the method. + * @return A method reference or null if the method was not found + */ + public static Method findMethod(Class cls, String methodName, + Class... parameterTypes) { + try { + return cls.getDeclaredMethod(methodName, parameterTypes); + } catch (Exception e) { + // Print the stack trace as + e.printStackTrace(System.err); + } + return null; + } +} diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java index 2e6a0d6e19..c624cf3224 100644 --- a/src/com/vaadin/ui/AbstractComponent.java +++ b/src/com/vaadin/ui/AbstractComponent.java @@ -16,6 +16,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.vaadin.Application; +import com.vaadin.event.ClientEventList; import com.vaadin.event.EventRouter; import com.vaadin.event.MethodEventSource; import com.vaadin.terminal.ErrorMessage; @@ -23,7 +24,9 @@ import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; import com.vaadin.terminal.Terminal; +import com.vaadin.terminal.gwt.client.ComponentEventHandler; import com.vaadin.terminal.gwt.server.ComponentSizeValidator; +import com.vaadin.tools.ReflectTools; /** * An abstract class that defines default implementation for the @@ -92,6 +95,12 @@ public abstract class AbstractComponent implements Component, MethodEventSource */ private EventRouter eventRouter = null; + /** + * The ClientEventList used for collecting client events which should be + * transmitted from the client to the server + */ + private ClientEventList clientEvents = null; + /** * The internal error message of the component. */ @@ -679,6 +688,22 @@ public abstract class AbstractComponent implements Component, MethodEventSource target.addAttribute("description", getDescription()); } + // davengo GmbH: add client event variables + String[] trigger = new String[] {}; + String[] events = new String[] {}; + if (clientEvents != null) { + events = clientEvents.getEvents(); + } + if (events.length != 0) { + target.addVariable(this, + ComponentEventHandler.HANDLER_TRIGGER_VARIABLE, + trigger); + target.addAttribute( + ComponentEventHandler.HANDLER_LISTEN_ATTRIBUTE, + events); + } + // end of event variables + paintContent(target); final ErrorMessage error = getErrorMessage(); @@ -813,6 +838,62 @@ public abstract class AbstractComponent implements Component, MethodEventSource } } + /** + * this method is executed when a event arrives from the event-api.
+ * this should be overridden by components which intend to use the event-api + * mechanism for registering Events. + * + * @param eventIdentifier + * @param parameters + */ + protected void handleEvent(String eventIdentifier, String[] parameters) { + // implemented by subclasses + } + + /** + * listens to the specified event type.
+ * on changes of the registered event by the client-side component, the + * method handleEvent(String event, String[] parameters) will be invoked (as + * long as either a listener is registered at the component for the given + * event type or the forceTransmission flag has been set by the client-side + * component).
+ *
+ * for every listener attached to this component this method has to be + * called once. the client-event list holds a counter on how-many listeners + * are listening for an event.
+ *
+ * this method should be executed by components which intend to use the + * event-api mechanism for listening on events. + * + * @param eventIdentifier + */ + private void listenEvent(String eventIdentifier) { + if (clientEvents == null) { + clientEvents = new ClientEventList(); + } + clientEvents.listenEvent(eventIdentifier); + requestRepaint(); + } + + /** + * stops listening to the specified event type.
+ *
+ * for every listener detached from this component this method has to be + * called once. the client-event list holds a counter on how-many listeners + * are listening for an event.
+ *
+ * this method should be executed by components which intend to use the + * event-api mechanism for listening on events. + * + * @param eventIdentifier + */ + private void unlistenEvent(String eventIdentifier) { + if (clientEvents != null) { + clientEvents.unlistenEvent(eventIdentifier); + requestRepaint(); + } + } + /* Component variable changes */ /* @@ -821,22 +902,138 @@ public abstract class AbstractComponent implements Component, MethodEventSource * interface. */ public void changeVariables(Object source, Map variables) { + + // handle events when triggered + if (variables + .containsKey(ComponentEventHandler.HANDLER_TRIGGER_VARIABLE)) { + String[] firedEvent = (String[]) variables + .get(ComponentEventHandler.HANDLER_TRIGGER_VARIABLE); + + // detect vaadin events + String[] params = new String[firedEvent.length - 1]; + + for (int i = 0; i < params.length; i++) { + params[i] = firedEvent[i + 1]; + } + + String event = firedEvent[0]; + + handleEvent(event, params); + + } + } /* General event framework */ - private static final Method COMPONENT_EVENT_METHOD; - - static { - try { - COMPONENT_EVENT_METHOD = Component.Listener.class - .getDeclaredMethod("componentEvent", - new Class[] { Component.Event.class }); - } catch (final java.lang.NoSuchMethodException e) { - // This should never happen - throw new java.lang.RuntimeException( - "Internal error finding methods in AbstractComponent"); + private static final Method COMPONENT_EVENT_METHOD = ReflectTools + .findMethod(Component.Listener.class, "componentEvent", + Component.Event.class); + + /** + *

+ * 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. + *

+ * + *

+ * This method additionally informs the event-api to route events with the + * given eventIdentifier to the components handleEvent function call. + *

+ * + *

+ * For more information on the inheritable event mechanism see the + * {@link com.vaadin.event com.vaadin.event package documentation}. + *

+ * + * @param eventIdentifier + * the identifier of the event to listen for + * @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. + */ + protected void addEventListener(String eventIdentifier, Class eventType, + Object object, Method method) { + if (eventRouter == null) { + eventRouter = new EventRouter(); } + eventRouter.addListener(eventType, object, method); + listenEvent(eventIdentifier); + } + + /** + * 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 object's methods that are + * registered to listen to events of type eventType generated + * by this component. + * + *

+ * This method additionally informs the event-api to stop routing events + * with the given eventIdentifier to the components handleEvent function + * call. + *

+ * + *

+ * For more information on the inheritable event mechanism see the + * {@link com.vaadin.event com.vaadin.event package documentation}. + *

+ * + * @param eventIdentifier + * the identifier of the event to stop listening for + * @param eventType + * the exact event type the object listens to. + * @param target + * the target object that has registered to listen to events of + * type eventType with one or more methods. + */ + protected void removeEventListener(String eventIdentifier, + Class eventType, Object target) { + if (eventRouter != null) { + eventRouter.removeListener(eventType, target); + } + unlistenEvent(eventIdentifier); + } + + /** + * 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. + * + *

+ * This method additionally informs the event-api to stop routing events + * with the given eventIdentifier to the components handleEvent function + * call. + *

+ * + *

+ * For more information on the inheritable event mechanism see the + * {@link com.vaadin.event com.vaadin.event package documentation}. + *

+ * + * @param eventIdentifier + * the identifier of the event to stop listening for + * @param eventType + * the exact event type the object listens to. + * @param target + * target object that has registered to listen to events of type + * eventType with one or more methods. + * @param method + * the method owned by target that's registered to + * listen to events of type eventType. + */ + protected void removeEventListener(String eventIdentifier, Class eventType, + Object target, Method method) { + if (eventRouter != null) { + eventRouter.removeListener(eventType, target, method); + } + unlistenEvent(eventIdentifier); } /** @@ -1012,12 +1209,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource * implemented interface. */ public void addListener(Component.Listener listener) { - if (eventRouter == null) { - eventRouter = new EventRouter(); - } - - eventRouter.addListener(Component.Event.class, listener, - COMPONENT_EVENT_METHOD); + addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD); } /* @@ -1026,10 +1218,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource * interface. */ public void removeListener(Component.Listener listener) { - if (eventRouter != null) { - eventRouter.removeListener(Component.Event.class, listener, - COMPONENT_EVENT_METHOD); - } + removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD); } /** diff --git a/src/com/vaadin/ui/AbstractOrderedLayout.java b/src/com/vaadin/ui/AbstractOrderedLayout.java index b43d155714..ed2f70d4f0 100644 --- a/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -4,14 +4,18 @@ package com.vaadin.ui; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; +import com.vaadin.event.ComponentEventListener; +import com.vaadin.event.MouseEvents.ClickEvent; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Sizeable; +import com.vaadin.tools.ReflectTools; @SuppressWarnings("serial") public abstract class AbstractOrderedLayout extends AbstractLayout implements @@ -307,4 +311,67 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements AlignmentUtils.setComponentAlignment(this, component, alignment); } + 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); + } + + /** + * 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 Component childComponent; + + public LayoutClickEvent(Component source, String mouseButton, + Component childComponent) { + super(source, mouseButton); + this.childComponent = childComponent; + } + + public Component getChildComponent() { + return childComponent; + } + + } + + public void addListener(LayoutClickListener listener) { + addEventListener("click", LayoutClickEvent.class, listener, + LayoutClickListener.clickMethod); + } + + public void removeListener(LayoutClickListener listener) { + removeEventListener("click", LayoutClickEvent.class, listener, + LayoutClickListener.clickMethod); + } + + @Override + protected void handleEvent(String event, String[] parameters) { + if (event.equals("click")) { + String button = parameters[0]; + String childComponentId = parameters[1]; + Component childComponent = null; + try { + int id = Integer.parseInt(childComponentId); + childComponent = components.get(id); + } catch (Exception e) { + // TODO: handle exception + } + + fireEvent(new LayoutClickEvent(this, button, childComponent)); + } + } } diff --git a/src/com/vaadin/ui/Component.java b/src/com/vaadin/ui/Component.java index ab7b7bb2ca..75d819f4b3 100644 --- a/src/com/vaadin/ui/Component.java +++ b/src/com/vaadin/ui/Component.java @@ -326,6 +326,15 @@ public interface Component extends Paintable, VariableOwner, Sizeable, public Event(Component source) { super(source); } + + /** + * Gets the Component where the event occurred. + * + * @return the Source of the event. + */ + public Component getComponent() { + return (Component) getSource(); + } } /** diff --git a/src/com/vaadin/ui/Embedded.java b/src/com/vaadin/ui/Embedded.java index 3b20641c5f..be60b744aa 100644 --- a/src/com/vaadin/ui/Embedded.java +++ b/src/com/vaadin/ui/Embedded.java @@ -7,6 +7,8 @@ package com.vaadin.ui; import java.util.Hashtable; import java.util.Iterator; +import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.event.MouseEvents.ClickListener; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; @@ -414,4 +416,20 @@ public class Embedded extends AbstractComponent { } } + public void addListener(ClickListener listener) { + addEventListener("click", ClickEvent.class, listener, + ClickListener.clickMethod); + } + + public void removeListener(ClickListener listener) { + removeEventListener("click", ClickEvent.class, listener, + ClickListener.clickMethod); + } + + @Override + protected void handleEvent(String event, String[] parameters) { + if (event.equals("click")) { + fireEvent(new ClickEvent(this, parameters[0])); + } + } } diff --git a/src/com/vaadin/ui/GridLayout.java b/src/com/vaadin/ui/GridLayout.java index d07e232b1a..510a8051d7 100644 --- a/src/com/vaadin/ui/GridLayout.java +++ b/src/com/vaadin/ui/GridLayout.java @@ -15,6 +15,8 @@ import java.util.Map.Entry; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.gwt.client.ui.VGridLayout; +import com.vaadin.ui.AbstractOrderedLayout.LayoutClickEvent; +import com.vaadin.ui.AbstractOrderedLayout.LayoutClickListener; /** *

@@ -72,7 +74,7 @@ public class GridLayout extends AbstractLayout implements /** * Mapping from components to their respective areas. */ - private final LinkedList components = new LinkedList(); + private final LinkedList components = new LinkedList(); /** * Mapping from components to alignments (horizontal + vertical). @@ -1299,4 +1301,34 @@ public class GridLayout extends AbstractLayout implements AlignmentUtils.setComponentAlignment(this, component, alignment); } + public void addListener(LayoutClickListener listener) { + addEventListener("click", LayoutClickEvent.class, listener, + LayoutClickListener.clickMethod); + } + + public void removeListener(LayoutClickListener listener) { + removeEventListener("click", LayoutClickEvent.class, listener, + LayoutClickListener.clickMethod); + } + + @Override + protected void handleEvent(String event, String[] parameters) { + if (event.equals("click")) { + String button = parameters[0]; + String childComponentRow = parameters[1]; + String childComponentCol = parameters[2]; + + Component childComponent = null; + try { + childComponent = getComponent(Integer + .parseInt(childComponentCol), Integer + .parseInt(childComponentRow)); + } catch (Exception e) { + // TODO: handle exception + } + + fireEvent(new LayoutClickEvent(this, button, childComponent)); + } + } + } diff --git a/src/com/vaadin/ui/Panel.java b/src/com/vaadin/ui/Panel.java index 03f1857386..2aaf697e85 100644 --- a/src/com/vaadin/ui/Panel.java +++ b/src/com/vaadin/ui/Panel.java @@ -11,6 +11,8 @@ import java.util.Map; import com.vaadin.event.Action; import com.vaadin.event.ShortcutAction; import com.vaadin.event.Action.Handler; +import com.vaadin.event.MouseEvents.ClickEvent; +import com.vaadin.event.MouseEvents.ClickListener; import com.vaadin.terminal.KeyMapper; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; @@ -543,4 +545,23 @@ public class Panel extends AbstractComponentContainer implements Scrollable, requestRepaint(); } } + + @Override + protected void handleEvent(String eventIdentifier, String[] parameters) { + if (eventIdentifier.equals("click")) { + fireEvent(new ClickEvent(this, parameters[0])); + } + } + + public void addListener(ClickListener listener) { + addEventListener("click", ClickEvent.class, listener, + ClickListener.clickMethod); + } + + public void removeListener(ClickListener listener) { + removeEventListener("click", ClickEvent.class, listener, + ClickListener.clickMethod); + + } + } diff --git a/src/com/vaadin/ui/TextField.java b/src/com/vaadin/ui/TextField.java index a773617265..94c97849c2 100644 --- a/src/com/vaadin/ui/TextField.java +++ b/src/com/vaadin/ui/TextField.java @@ -8,6 +8,10 @@ import java.text.Format; import java.util.Map; import com.vaadin.data.Property; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.gwt.client.ui.VTextField; @@ -547,4 +551,33 @@ public class TextField extends AbstractField { requestRepaint(); } + @Override + protected void handleEvent(String eventIdentifier, String[] parameters) { + if (eventIdentifier.equals("focus")) { + fireEvent(new FocusEvent(this)); + } else if (eventIdentifier.equals("blur")) { + fireEvent(new BlurEvent(this)); + } + } + + public void addListener(FocusListener listener) { + addEventListener("focus", FocusEvent.class, listener, + FocusListener.focusMethod); + } + + public void removeListener(FocusListener listener) { + removeEventListener("focus", FocusEvent.class, listener, + FocusListener.focusMethod); + } + + public void addListener(BlurListener listener) { + addEventListener("blur", BlurEvent.class, listener, + BlurListener.blurMethod); + } + + public void removeListener(BlurListener listener) { + removeEventListener("blur", BlurEvent.class, listener, + BlurListener.blurMethod); + } + } -- 2.39.5