/* Events.java Purpose: Description: History: Fri Jul 22 15:49:51 2005, Created by tomyeh Copyright (C) 2005 Potix Corporation. All Rights Reserved. {{IS_RIGHT This program is distributed under LGPL Version 2.1 in the hope that it will be useful, but WITHOUT ANY WARRANTY. }}IS_RIGHT */ package org.zkoss.zk.ui.event; import org.zkoss.zk.au.out.AuEcho; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.UiException; import org.zkoss.zk.ui.impl.EventProcessor; import org.zkoss.zk.ui.sys.ComponentCtrl; import org.zkoss.zk.ui.sys.ComponentsCtrl; import org.zkoss.zk.ui.sys.DesktopCtrl; import org.zkoss.zk.ui.sys.EventProcessingThread; import org.zkoss.zk.ui.sys.ExecutionCtrl; import org.zkoss.zk.ui.util.Clients; /** * Utilities to handle events. * * @author tomyeh */ public class Events { private Events() { } //prevent from creation /** The onClick event (used with {@link MouseEvent}). */ public static final String ON_CLICK = "onClick"; /** The onRightClick event (used with {@link MouseEvent}). */ public static final String ON_RIGHT_CLICK = "onRightClick"; /** The onDoubleClick event (used with {@link MouseEvent}). */ public static final String ON_DOUBLE_CLICK = "onDoubleClick"; /** The onMouseOver event (used with {@link MouseEvent}). * <p>Notice, if Internet connection is too far, the user might already move * the mouse out of a widget when the server receives onMouseOver. * @since 5.0.3 */ public static final String ON_MOUSE_OVER = "onMouseOver"; /** The onMouseOut event (used with {@link MouseEvent}). * @since 5.0.3 */ public static final String ON_MOUSE_OUT = "onMouseOut"; /** The onOK event (used with {@link KeyEvent}). */ public static final String ON_OK = "onOK"; /** The onCancel event (used with {@link KeyEvent}). */ public static final String ON_CANCEL = "onCancel"; /** The onCtrlKey event (used with {@link KeyEvent}). */ public static final String ON_CTRL_KEY = "onCtrlKey"; /** The onChange event (used with {@link InputEvent}). */ public static final String ON_CHANGE = "onChange"; /** The onChanging event (used with {@link InputEvent}). */ public static final String ON_CHANGING = "onChanging"; /** The onError event (used with {@link ErrorEvent}). */ public static final String ON_ERROR = "onError"; /** The onScroll event (used with {@link ScrollEvent}). */ public static final String ON_SCROLL = "onScroll"; /** The onScrolling event (used with {@link ScrollEvent}). */ public static final String ON_SCROLLING = "onScrolling"; /** The onSwipe event (used with {@link SwipeEvent}). * @since 6.5.0 */ public static final String ON_SWIPE = "onSwipe"; /** The onSelect event (used with {@link SelectEvent}). */ public static final String ON_SELECT = "onSelect"; /** The onSelection event (used with {@link SelectionEvent}). */ public static final String ON_SELECTION = "onSelection"; /** The onCheck event (used with {@link CheckEvent}). */ public static final String ON_CHECK = "onCheck"; /** The onMove event (used with {@link MoveEvent}). */ public static final String ON_MOVE = "onMove"; /** The onSize event (used with {@link SizeEvent}). */ public static final String ON_SIZE = "onSize"; /** The onAfterSize event (used with {@link AfterSizeEvent}). * @since 6.5.2 */ public static final String ON_AFTER_SIZE = "onAfterSize"; /** The onZIndex event (used with {@link ZIndexEvent}). */ public static final String ON_Z_INDEX = "onZIndex"; /** The onOpen event (used with {@link OpenEvent}). */ public static final String ON_OPEN = "onOpen"; /** The onClose event (used with {@link Event}) * used to denote the close button is pressed. */ public static final String ON_CLOSE = "onClose"; /** The onRender request. * There is no event associated with this AU request. */ public static final String ON_RENDER = "onRender"; /** The onTimer event (used with {@link Event}). * Sent when a timer is up. */ public static final String ON_TIMER = "onTimer"; /** The onFocus event (used with {@link Event}). * Sent when a component gets a focus. */ public static final String ON_FOCUS = "onFocus"; /** The onBlur event (used with {@link Event}). * Sent when a component loses a focus. */ public static final String ON_BLUR = "onBlur"; /** The onDrop event (used with {@link DropEvent}). * Sent when a component is dragged and drop to another. */ public static final String ON_DROP = "onDrop"; /** The onNotify event (used with {@link Event}). * It is not used by any component, but it is, rather, designed to * let users add customized events. */ public static final String ON_NOTIFY = "onNotify"; /** The onSort event (used with {@link Event}) * to notify a request for sorting. */ public static final String ON_SORT = "onSort"; /** The onGroup event (used with {@link Event}) * to notify a request for grouping. * @since 5.0.0 */ public static final String ON_GROUP = "onGroup"; /** The onUngroup event (used with {@link Event}) * to notify a request for ungrouping. * @since 6.5.0 */ public static final String ON_UNGROUP = "onUngroup"; /** The onUpload event (used with {@link UploadEvent}. */ public static final String ON_UPLOAD = "onUpload"; /** The onBookmarkChange event (used with {@link BookmarkEvent}) * to notify that user pressed BACK, FORWARD or specified URL directly * that causes the bookmark is changed (but still in the same desktop). * <p>All root components of all pages of the desktop will * receive this event. * @since 3.0.8 */ public static final String ON_BOOKMARK_CHANGE = "onBookmarkChange"; /** The onURIChange event (used with {@link URIEvent}) * to notify that the associated URI of a component is changed. * Currently only the iframe component supports this event. * @since 3.5.0 */ public static final String ON_URI_CHANGE = "onURIChange"; /** The onClientInfo event (used with {@link ClientInfoEvent}). */ public static final String ON_CLIENT_INFO = "onClientInfo"; /** The onVisibilityChange event (used with {@link VisibilityChangeEvent}). * Only for browsers that support HTML 5 Page Visibility API * @since 6.5.1 */ public static final String ON_VISIBILITY_CHANGE = "onVisibilityChange"; /** The onCreate event (used with {@link CreateEvent}) to notify a component * that it (and its children) has been created by ZK's evaluating a ZUML page. */ public static final String ON_CREATE = "onCreate"; /** The onModal event (used with {@link Event}) to notify a component * shall become modal. Currently, only ZUL's window components support it. * <p>Notice that it is not fired if the event thread is disabled (default). */ public static final String ON_MODAL = "onModal"; /** The onPiggyback event (used with {@link Event}) used to notify * a root component that the client has sent a request to the server. * It is meaningful only if it is registered to the root component. * Once registered, it is called * each time the client sends a request to the server. * The onPiggyback's event listener is processed after all other * events are processed. * * <p>The onPiggyback event is designed to let developers piggyback * the least-emergent UI updates to the client. * * @since 2.4.0 */ public static final String ON_PIGGYBACK = "onPiggyback"; /** The onUser event. It is a generic event that an application developer * might send from the client. ZK doesn't use this event. */ public static final String ON_USER = "onUser"; /** The onMinimize event (used with {@link MinimizeEvent}). * @since 3.5.0 */ public static final String ON_MINIMIZE = "onMinimize"; /** The onMaximize event (used with {@link MaximizeEvent}). * @since 3.5.0 */ public static final String ON_MAXIMIZE = "onMaximize"; /** The onFulfill event (used with {@link FulfillEvent}) * to denote a fulfill condition has been applied. * Developer usually listens to this event to process the * new created children. For example, you might invoke * {@link org.zkoss.zk.ui.Components#wireFellows} to process these new components. * @since 3.0.8 */ public static final String ON_FULFILL = "onFulfill"; /** The onStub event (used with {@link StubEvent}) * to denote an event sent from the peer widget of a stub component * (at the client). * @since 6.0.0 */ public static final String ON_STUB = "onStub"; /** The onDesktopRecycle event (used with {@link Event}) * to notify that a desktop has been recycled. * All top-level components will receive this event when * recycled. * <p>Refer to {@link org.zkoss.zk.ui.util.DesktopRecycle} for desktop * recycling. * @since 5.0.2 */ public static final String ON_DESKTOP_RECYCLE = "onDesktopRecycle"; /** * The onDeferredEvaluation event (used with {@link Event}) * to notify a component the {@link org.zkoss.zk.ui.util.Composer#doAfterCompose(Component)} phase is done, * and can evaluate deferred expressions if any. * @since 8.0.0 */ public static final String ON_DEFERRED_EVALUATION = "onDeferredEvaluation"; /** * The onSlide event (used with {@link Event}). * @since 8.0.2 */ public static final String ON_SLIDE = "onSlide"; /** Returns whether an event name is valid. * * <p>The event name must start with on and the third character * must be upper case. */ public static final boolean isValid(String name) { return name != null && name.length() > 2 && name.charAt(0) == 'o' && name.charAt(1) == 'n' && Character.isUpperCase(name.charAt(2)); } /** Returns whether the current thread is an event listener. */ public static final boolean inEventListener() { return EventProcessor.inEventListener(); } /** Returns whether an event handler or listener is available for * the specified component and event. * * <p>A event handler is either a public method named onXxx or * a ZUL attribute named onXxx, where onXxx is the event name. * A event listener is {@link EventListener} being added * by {@link Component#addEventListener} and {@link Page#addEventListener}. * * <p>Unlike {@link Component#isListenerAvailable}, which checks * only the event listener, this method * check both event handlers and listeners, i.e., * the onXxx members defined in ZUML, the onXxx method defined * in the implementation class, and the event listener registered. * * @param asap whether to check only non-deferrable listener, * i.e., not implementing {@link org.zkoss.zk.ui.event.Deferrable}, * or {@link org.zkoss.zk.ui.event.Deferrable#isDeferrable} is false. * @see org.zkoss.zk.ui.event.Deferrable * @see Component#isListenerAvailable */ public static boolean isListened(Component comp, String evtnm, boolean asap) { if (((ComponentCtrl) comp).getEventHandler(evtnm) != null) return true; if (ComponentsCtrl.getEventMethod(comp.getClass(), evtnm) != null || comp.isListenerAvailable(evtnm, asap)) return true; if (!asap) { final Page page = comp.getPage(); return page != null && page.isListenerAvailable(evtnm); } return false; } /** Sends the event to the specified component and process it * immediately. This method can only be called when processing an event. * It is OK to send event to component from another page as long as * they are in the same desktop. */ public static void sendEvent(Component comp, Event event) { final Execution exec = Executions.getCurrent(); final Desktop desktop = exec.getDesktop(); //note: we don't use comp.getDesktop because 1) it may be null //2) it may be different from the current desktop event = ((DesktopCtrl) desktop).beforeSendEvent(event); if (event == null) return; //done final Thread thd = Thread.currentThread(); if (!(thd instanceof EventProcessingThread)) { if (!desktop.getWebApp().getConfiguration().isEventThreadEnabled()) { final ExecutionCtrl execCtrl = (ExecutionCtrl) exec; final Page page = execCtrl.getCurrentPage(); final EventProcessor proc = new EventProcessor(desktop, comp, event); proc.setup(); try { proc.process(); } catch (Exception ex) { throw UiException.Aide.wrap(ex); } finally { execCtrl.setCurrentPage(page); } return; //done } throw new UiException("Callable only in the event listener"); } try { ((EventProcessingThread) thd).sendEvent(comp, event); } catch (Exception ex) { throw UiException.Aide.wrap(ex); } } /** Sends the event to the target specified in the event, and processes it immediately. * * <p>Note: {@link Event#getTarget} cannot be null. */ public static void sendEvent(Event event) { sendEvent(event.getTarget(), event); } /** Sends the event to the target, and processes it immediately. * @param target the target of the event (never null) * @since 5.0.4 */ public static void sendEvent(String name, Component target, Object data) { sendEvent(new Event(name, target, data)); } /** Posts an event to the current execution. * * <p>The priority of the event is assumed to be 0. Refer to * {@link #postEvent(int, Event)}. * * <p>On the other hand, the event sent by {@link #sendEvent} is processed * immediately without posting it to the queue. * * <p>Note: if the target of an event is not attached to * the page yet, the event is ignored silently. * @see #sendEvent * @see #echoEvent(String, Component, Object) * @see #postEvent(int, Event) */ public static final void postEvent(Event event) { Executions.getCurrent().postEvent(event); } /** Posts an instance of {@link Event} to the current execution. * <p>The priority of the event is assumed to be 0. Refer to * {@link #postEvent(int, String, Component, Object)}. * @see #postEvent(Event) * @see #postEvent(int, String, Component, Object) */ public static final void postEvent(String name, Component target, Object data) { postEvent(0, name, target, data); } /** Posts an event to the current execution with the specified priority. * * <p>The posted events are processed from the higher priority to the * lower one. If two events are posted with the same priority, * the earlier the event being posted is processed earlier * (first-in-first-out). * * @param priority the priority of the event. The higher the priority is, the earlier it * is handled.<br/> * The priority posted by {@link #postEvent(Event)} is 0. * Applications shall not use the priority higher than 10,000 and * lower than -10,000 since they are reserved for component development. * @since 3.0.7 */ public static final void postEvent(int priority, Event event) { Executions.getCurrent().postEvent(priority, event); } /** Queues the give event for the specified target to this execution. * The target could be different from {@link Event#getTarget}. * @param priority the priority of the event. The default priority is 0 * and the higher value means higher priority. * @param realTarget the target component that will receive the event. * If null, it means broadcast, i.e., all root components will receive * this event. * <br/>Notice that postEvent(n, event) is the same as postEvent(n, event.getTarget(), event), * but different from postEvent(n, 0, event). * @since 5.0.7 */ public static final void postEvent(int priority, Component realTarget, Event event) { Executions.getCurrent().postEvent(priority, realTarget, event); } /** Queues the give event for the specified target to this execution. * The target could be different from {@link Event#getTarget}. * @param realTarget the target component that will receive the event. * If null, it means broadcast, i.e., all root components will receive * this event. * <br/>Notice that postEvent(n, event) is the same as postEvent(n, event.getTarget(), event), * but different from postEvent(n, 0, event). * @since 5.0.7 */ public static final void postEvent(Component realTarget, Event event) { Executions.getCurrent().postEvent(0, realTarget, event); } /** Posts an instance of {@link Event} to the current execution * with the specified priority. * * <p>The posted events are processed from the higher priority to the * lower one. If two events are posted with the same priority, * the earlier the event being posted is processed earlier * (first-in-first-out). * * <p>The priority posted by posted by {@link #postEvent(Event)} is * 0. * Applications shall not use the priority higher than 10,000 and * lower than -10,000 since they are reserved for component * development. * * @param priority the priority of the event. * @since 3.0.7 */ public static final void postEvent(int priority, String name, Component target, Object data) { if (name == null || name.length() == 0 || target == null) throw new IllegalArgumentException("Name is empty or target is null."); postEvent(priority, new Event(name, target, data)); } /** Echos an event. * By echo we mean the event is fired after the client receives the AU * responses and then echoes back. * In other words, the event won't be processed in the current execution. * Rather, it executes after the client receives the AU responses * and then echoes back the event back. * * <p>It is usually if you want to prompt the user before doing a long * operation. A typical case is to open a highlighted window to * prevent the user from clicking any button before the operation gets done. * * <p>It is the same as <code>echoEvent(name, target, (Object)data)</code>. * * @since 3.0.2 * @see #sendEvent * @see #echoEvent(String, Component, Object) * @param name the event name, such as onSomething * @param target the component to receive the event (never null). * @param data the extra information, or null if not available. * It will become {@link Event#getData}. */ public static final void echoEvent(String name, Component target, String data) { echoEvent(name, target, (Object) data); } /** Echos an event. * By echo we mean the event is fired after the client receives the AU * responses and then echoes back. * In other words, the event won't be processed in the current execution. * Rather, it executes after the client receives the AU responses * and then echoes back the event back. * * <p>It is usually if you want to prompt the user before doing a long * operation. A typical case is to open a highlighted window to * prevent the user from clicking any button before the operation gets done. * * @since 5.0.4 * @see #sendEvent */ public static final void echoEvent(Event event) { echoEvent(event.getName(), event.getTarget(), event.getData()); } /** Echos an event. * By echo we mean the event is fired after the client receives the AU * responses and then echoes back. * In other words, the event won't be processed in the current execution. * Rather, it executes after the client receives the AU responses * and then echoes back the event back. * * <p>It is usually if you want to prompt the user before doing a long * operation. A typical case is to open a highlighted window to * prevent the user from clicking any button before the operation gets done. * * @since 5.0.4 * @see #sendEvent * @param name the event name, such as onSomething * @param target the component to receive the event (never null). * @param data the extra information, or null if not available. * It will become {@link Event#getData}. */ public static final void echoEvent(String name, Component target, Object data) { if (name == null || name.length() == 0 || target == null) throw new IllegalArgumentException("Name is empty or target is null."); Clients.response(new AuEcho(target, name, data)); } /** <p>Add onXxx event handler defined in controller object to the specified * component. * The controller is a POJO file with onXxx methods(the event handler codes). * This utility method registers these onXxx events to the specified * component so you don't have to implement and add {@link EventListener} * into the component one by one.</p> * * <p>All public methods whose names start with "on" in controller object * are considered * as event handlers and the corresponding event is listened. * For example, if the controller object has a method named onOK, * then the onOK event is listened and the onOK method is called * when the event is received. * <p>Since 3.0.8, this method treats ForwardEvent specially. If the * event argument going to be passed into the onXxx event listener is a * ForwardEvent and the onXxx event listener defined in the controller * specifies a specific event class as its parameter rather than generic * Event or ForwardEvent class, then this method will unwrap the * ForwardEvent automatically * (see {@link org.zkoss.zk.ui.event.ForwardEvent#getOrigin()}) * and pass the original forwarded event to the defined onXxx event listener.</p> * <p>This is a useful tool for MVC design practice. You can write * onXxx event handler codes in controller object and use this utility to * register the events to the specified component.</p> * * @param comp the component to be registered the events * @param controller a POJO file with onXxx methods(event handlers) * @since 3.0.6 * @see GenericEventListener */ public static final void addEventListeners(Component comp, final Object controller) { final GenericEventListener evtl = new GenericEventListener() { protected Object getController() { return controller; } }; evtl.bindComponent(comp); } /** Returns the real origin event of a forwarded event. * By real we mean the last non-forward event in the chain of * {@link ForwardEvent#getOrigin}. * Notice that a forward event might be forwarded again, so * {@link ForwardEvent#getOrigin} might not be the real origin. * @since 3.5.1 */ public static final Event getRealOrigin(ForwardEvent event) { for (;;) { Event evt = event.getOrigin(); if (!(evt instanceof ForwardEvent)) return evt; event = (ForwardEvent) evt; } } }