/* * Ext GWT - Ext for GWT * Copyright(c) 2007-2009, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ package com.extjs.gxt.ui.client.widget; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.extjs.gxt.ui.client.GXT; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.HideMode; import com.extjs.gxt.ui.client.aria.FocusFrame; import com.extjs.gxt.ui.client.core.El; import com.extjs.gxt.ui.client.core.FastMap; import com.extjs.gxt.ui.client.core.XDOM; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.BaseObservable; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.EventType; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.Observable; import com.extjs.gxt.ui.client.event.WidgetListener; import com.extjs.gxt.ui.client.state.StateManager; import com.extjs.gxt.ui.client.util.DelayedTask; import com.extjs.gxt.ui.client.util.SwallowEvent; import com.extjs.gxt.ui.client.widget.layout.LayoutData; import com.extjs.gxt.ui.client.widget.menu.Menu; import com.extjs.gxt.ui.client.widget.tips.ToolTip; import com.extjs.gxt.ui.client.widget.tips.ToolTipConfig; import com.google.gwt.event.dom.client.DomEvent; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Widget; /** * Base class for GXT components. All subclasses of Component can automatically * participate in the standard GXT component lifecycle of creation, attach and * detach. They also have automatic support for basic hide/show and * enable/disable behavior. Component allows any subclass to be lazy-rendered * into any GXT {@link Container}. Components added to a GWT {@link Panel} will * be rendered when inserted. All visual widgets that require rendering into a * layout should subclass Component (or {@link BoxComponent} if managed box * model handling is required). * * <p /> * The following 4 methods inherited from UIObject (setSize, setWidth, * setHeight, setPixelSize) have been overridden and do nothing. Any component * whose size can change should subclass {@link BoxComponent}. * * <p /> * All components are registered and unregistered with the @link * {@link ComponentManager} when the are attached and detached. * * <dl> * <dt>Events:</dt> * * <dd><b>Enable</b> : ComponentEvent(component)<br> * <div>Fires after the component is enabled.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>Disable</b> : ComponentEvent(component)<br> * <div>Fires after the component is disabled.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>BeforeHide</b> : ComponentEvent(component)<br> * <div>Fires before the component is hidden. Listeners can cancel the action by * calling {@link BaseEvent#setCancelled(boolean)}.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>BeforeShow</b> : ComponentEvent(component)<br> * <div>Fires before the component is shown. Listeners can cancel the action by * calling {@link BaseEvent#setCancelled(boolean)}.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>Hide</b> : ComponentEvent(component)<br> * <div>Fires after the component is hidden.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>Show</b> : ComponentEvent(component)<br> * <div>Fires after the component is shown.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>Attach</b> : ComponentEvent(component)<br> * <div>Fires after the component is attached.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>Detach</b> : ComponentEvent(component) <br> * <div>Fires after the component is detached.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>BeforeRender</b> : ComponentEvent(component)<br> * <div>Fires before the component is rendered. Listeners can cancel the action * by calling {@link BaseEvent#setCancelled(boolean)}.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>Render</b> : ComponentEvent(component)<br> * <div>Fires after the component is rendered.</div> * <ul> * <li>component : this</li> * </ul> * </dd> * * <dd><b>BrowserEvent</b> : ComponentEvent(component, event)<br> * <div>Fires on any browser event the component receives. Listeners will be * called prior to any event processing and before * {@link #onComponentEvent(ComponentEvent)} is called. Listeners can call * {@link BaseEvent#setCancelled(boolean)} to cancel the processing of the * event.</div> * <ul> * <li>component : this</li> * <li>event : event</li> * </ul> * </dd> * * <dd><b>BeforeStateRestore</b> : ComponentEvent(component, state)<br> * <div>Fires before the state of the component is restored. Listeners can * cancel the action by calling {@link BaseEvent#setCancelled(boolean)}.</div> * <ul> * <li>component : this</li> * <li>state : the state values * </ul> * </dd> * * <dd><b>StateRestore</b> : ComponentEvent(component, state)<br> * <div>Fires after the state of the component is restored.</div> * <ul> * <li>component : this</li> * <li>state : map of state key / value pairs</li> * </ul> * </dd> * * <dd><b>BeforeStateSave</b> : ComponentEvent(component, state)<br> * <div>Fires before the state of the component is saved to the configured state * provider.</div> * <ul> * <li>component : this</li> * <li>state : map of state key / value pairs</li> * </ul> * </dd> * * <dd><b>StateSave</b> : ComponentEvent(component, state)<br> * <div>Fires after the state of the component is saved to the configured state * provider.</div> * <ul> * <li>component : this</li> * <li>state : map of state key / value pairs</li> * </ul> * </dd> * * </dl> * * @see ComponentManager */ public abstract class Component extends Widget implements Observable { static { GXT.init(); } /** * The base style is typically set as the component's style when rendered. All * child styles should be calculated based on the base style when the * component is rendered. This allows a component's style to be swapped by * simply modifying the base style (defaults to null). */ protected String baseStyle; /** * True if the component is disabled. Read only. */ protected boolean disabled; /** * The style used when a component is disabled (defaults to * 'x-item-disabled'). */ protected String disabledStyle = "x-item-disabled"; protected Element dummy; /** * True if the component is can receive focus (defaults to false). A hidden * input field will be created created for Safari. */ protected boolean focusable; /** * True if this component is hidden. Read only. */ protected boolean hidden; protected boolean hideParent; /** * True if this component has been rendered. Read-only. */ protected boolean rendered; /** * True to enable state (defaults to true). */ protected boolean stateful = true; /** * The state id (defaults to null). */ protected String stateId; protected List<SwallowEvent> swallowEvents; protected ToolTip toolTip; protected boolean mask; protected String maskMessage; protected String maskMessageStyleName; protected List<ComponentAttachable> attachables; private boolean afterRender; private int borders = Style.DEFAULT; private Menu contextMenu; private Map<String, Object> dataMap; private boolean disableBrowserEvents; private int disableContextMenu = Style.DEFAULT; private boolean disableEvents; private int disableTextSelection = Style.DEFAULT; private El el; private boolean enableState = true; private int events; private boolean focused; private El focusEl; private HideMode hideMode = HideMode.DISPLAY; private String id, itemId, cls, title; private LayoutData layoutData; private ModelData model; private Observable observable; private Map<String, String> overElements; private List<ComponentPlugin> plugins; private boolean setElementRender; private Map<String, Object> state; private String styles = ""; private ToolTipConfig toolTipConfig; protected boolean monitorWindowResize; protected int windowResizeDelay = 100; protected DelayedTask windowResizeTask; protected HandlerRegistration resizeHandler; /** * Creates a new component.. */ public Component() { observable = createObservable(); } /** * Specialized constructor for creating components from existing nodes. The * given element should be part of the dom and have a a parent element. * * @param element the element * @param attach true to attach the component */ protected Component(Element element, boolean attach) { this(); setElement(element); render(DOM.getParent(element)); if (attach) { onAttach(); } } /** * Appends an event handler to this component. * * @param eventType the eventType * @param listener the listener to be added */ public void addListener(EventType eventType, Listener<? extends BaseEvent> listener) { observable.addListener(eventType, listener); } /** * Adds a component plugin. * * @param plugin the component plugin */ public void addPlugin(ComponentPlugin plugin) { assertPreRender(); if (plugins == null) { plugins = new ArrayList<ComponentPlugin>(); } plugins.add(plugin); } /** * Adds a CSS style name to the component's underlying element. * * @param style the CSS style name to add */ public void addStyleName(String style) { if (rendered) { fly(getStyleElement()).addStyleName(style); } else { cls = cls == null ? style : cls + " " + style; } } /** * Adds a listener to receive widget events. * * @param listener the listener to be added */ public void addWidgetListener(WidgetListener listener) { addListener(Events.Attach, listener); addListener(Events.Detach, listener); addListener(Events.Resize, listener); } /** * Clears the component's state. */ public void clearState() { if (state != null) { state.clear(); saveState(); } } /** * Disable this component. Fires the <i>Disable</i> event. */ public void disable() { if (rendered) { onDisable(); } disabled = true; fireEvent(Events.Disable); } /** * True to disable event processing. * * @param disable true to disable */ public void disableEvents(boolean disable) { disableEvents = disable; } /** * Enables and disables text selection for the component. * * @param disable <code>true</code> to disable text selection */ public void disableTextSelection(boolean disable) { disableTextSelection = disable ? 1 : 0; if (isAttached()) { el.disableTextSelection(disable); } } /** * Returns the component's el instance. * * @return the el instance */ public El el() { assertAfterRender(); return el; } /** * Enable this component. Fires the <i>Enable</i> event. */ public void enable() { if (rendered) { onEnable(); } disabled = false; fireEvent(Events.Enable); } /** * Enables or disables event processing. * * @param enable the enable state */ public void enableEvents(boolean enable) { disableEvents = !enable; } /** * Fires an event with the given event type. * * @param type the event type * @return <code>false</code> if any listeners return <code>false</code> */ public boolean fireEvent(EventType type) { if (disableEvents || !hasListeners(type)) return true; ComponentEvent be = createComponentEvent(null); be.setType(type); return fireEvent(type, be); } public boolean fireEvent(EventType eventType, BaseEvent be) { if (disableEvents || !hasListeners(eventType)) return true; return observable.fireEvent(eventType, be); } /** * Fires the specified event with the given event type. * * @param type the event type * @param ce the base event * @return <code>false</code> if any listeners return <code>false</code> */ public boolean fireEvent(EventType type, ComponentEvent ce) { if (disableEvents || !hasListeners(type)) return true; return observable.fireEvent(type, previewEvent(type, ce)); } /** * Returns the global flyweight instance. * * @param elem the new wrapped dom element * @return the global flyweight instance */ public El fly(Element elem) { return El.fly(elem, "component"); } /** * Try to focus this component. Fires the <i>Focus</i> event. */ public void focus() { this.focused = true; if (rendered) { getFocusEl().focus(); } fireEvent(Events.Focus); } /** * Returns the component's base style. * * @return the base style */ public String getBaseStyle() { return baseStyle; } /** * Returns the component's border state. * * @return true if borders are visible */ public boolean getBorders() { return borders > 0; } /** * Returns the application defined property for the given name, or * <code>null</code> if it has not been set. * * @param key the name of the property * @return the value or <code>null</code> if it has not been set */ @SuppressWarnings("unchecked") public <X> X getData(String key) { if (dataMap == null) return null; return (X) dataMap.get(key); } @Override public Element getElement() { // if getElement is called before a component is rendered then the caller is // a gwt panel. a proxy element is returned and the component will be // rendered when it is attached if (!rendered) { if (dummy == null) dummy = DOM.createDiv(); return dummy; } return super.getElement(); } /** * Returns the component's hide mode. * * @return the hide mode */ public HideMode getHideMode() { return hideMode; } /** * Returns the id of this component. A new id is generated if an id has not * been set. * * @return the component's id */ public String getId() { if (id == null) { id = XDOM.getUniqueId(); setId(id); return id; } return id; } /** * Returns the item id of this component. Unlike the component's id, the item * id does not have to be unique. * * @return the component's item id */ public String getItemId() { return itemId != null ? itemId : getId(); } public List<Listener<? extends BaseEvent>> getListeners(EventType eventType) { return observable.getListeners(eventType); } /** * Returns the component's model. * * @return the model */ @SuppressWarnings("unchecked") public <X> X getModel() { return (X) model; } /** * Returns the component's state. * * @return the state */ public Map<String, Object> getState() { if (!enableState || state == null) { state = new FastMap<Object>(); } return state; } /** * Returns the component's tool tip. * * @return the tool tip */ public ToolTip getToolTip() { if (toolTip == null && toolTipConfig != null) { toolTip = new ToolTip(this, toolTipConfig); } return toolTip; } public boolean hasListeners() { return observable.hasListeners(); } public boolean hasListeners(EventType eventType) { return observable.hasListeners(eventType); } /** * Hide this component. Fires the <i>BeforeHide</i> event before the component * is hidden, the fires the <i>Hide</i> event after the component is hidden. */ public void hide() { if (fireEvent(Events.BeforeHide)) { hidden = true; if (rendered) { onHide(); } fireEvent(Events.Hide); } } /** * Hides the component's tool tip (if one exists). */ public void hideToolTip() { if (toolTip != null) { toolTip.hide(); } } /** * Returns true if events are disabled. * * @return true if events disabled */ public boolean isDisabledEvents() { return disableEvents; } /** * Returns <code>true</code> if the component is enabled. * * @return the enabled state */ public boolean isEnabled() { return !disabled; } /** * Returns <code>true</code> if the component is rendered. * * @return the rendered state */ public boolean isRendered() { return rendered; } /** * Returns <code>true</code> if the component is visible. */ public boolean isVisible() { return rendered && !hidden && el().isVisible(true); } /** * Puts a mask over this component to disable user interaction. * * @return the mask element */ public El mask() { return mask(null, null); } /** * Puts a mask over this component to disable user interaction. * * @param message a message to display in the mask * @return the mask element */ public El mask(String message) { return mask(message, null); } /** * Puts a mask over this component to disable user interaction. * * @param message a message to display in the mask * @param messageStyleName a CSS style name to be applied to the message text * @return the mask element */ public El mask(String message, String messageStyleName) { mask = true; maskMessage = message; maskMessageStyleName = messageStyleName; if (rendered) { return el().mask(message, messageStyleName); } return null; } /** * Components delegate event handling to * {@link #onComponentEvent(ComponentEvent)}. Subclasses should not override. * * @param event the dom event */ public void onBrowserEvent(Event event) { if (disabled || disableEvents || disableBrowserEvents) { return; } int type = DOM.eventGetType(event); Element eventTarget = (Element) event.getEventTarget().cast(); if (swallowEvents != null) { for (SwallowEvent e : swallowEvents) { if (e.getEventType().getEventCode() == type && e.getElement().isOrHasChild(eventTarget)) { event.stopPropagation(); if (e.isPreventDefault()) { event.preventDefault(); } } } } // hack to receive keyboard events in safari if (GXT.isWebKit && type == Event.ONCLICK && focusable) { if (getElement().getTagName().equals("input") || eventTarget.getPropertyString("__eventBits") == null) { focus(); } } EventType eventType = Events.lookupBrowserEvent(type); ComponentEvent ce = createComponentEvent(event); ce.setEvent(event); ce.setType(eventType); // browser event listeners can cancel event if (!fireEvent(Events.BrowserEvent, ce)) { return; } if (type == (GXT.isSafari && GXT.isMac ? Event.ONMOUSEDOWN : Event.ONMOUSEUP) && ce.isRightClick()) { onRightClick(ce); } // specialized support for mouse overs if (overElements != null && (type == Event.ONMOUSEOVER || type == Event.ONMOUSEOUT)) { El target = ce.getTargetEl(); String style = overElements.get(target.getId()); if (style != null) { target.setStyleName(style, type == Event.ONMOUSEOVER); } } onComponentEvent(ce); fireEvent(eventType, ce); DomEvent.fireNativeEvent(event, this, getElement()); } /** * Any events a component receives will be forwarded to this method. * Subclasses should override as needed. The {@link #onBrowserEvent} method * should not be overridden or modified. * * @param ce the base event */ public void onComponentEvent(ComponentEvent ce) { } /** * Called when the component is in a LayoutContainer and the container's * layout executes. This method will not be called on container instances. * Default implementation does nothing. */ public void recalculate() { } /** * Removes all listeners. */ public void removeAllListeners() { observable.removeAllListeners(); } @SuppressWarnings("unchecked") @Override public void removeFromParent() { if (getParent() instanceof Container) { ((Container) getParent()).remove(this); return; } super.removeFromParent(); } /** * Removes a listener. * * @param eventType the event type * @param listener the listener to be removed */ public void removeListener(EventType eventType, Listener<? extends BaseEvent> listener) { observable.removeListener(eventType, listener); } /** * Removes a CSS style name from the component's underlying element. * * @param style the CSS style name to remove */ public void removeStyleName(String style) { if (rendered) { fly(getStyleElement()).removeStyleName(style); } else if (style != null && cls != null) { String[] s = cls.split(" "); cls = ""; for (int i = 0; i < s.length; i++) { if (!s[i].equals(style)) { cls += " " + s[i]; } } } } /** * Removes a swallow event. * * @param e the swallow event to be removed */ public void removeSwallow(SwallowEvent e) { swallowEvents.remove(e); } /** * Removes a listener. * * @param listener the listener to be removed */ public void removeWidgetListener(WidgetListener listener) { if (observable.hasListeners()) { observable.removeListener(Events.Attach, listener); observable.removeListener(Events.Detach, listener); observable.removeListener(Events.Resize, listener); } } /** * Renders the element. Typically, this method does not need to be called * directly. A component will rendered by its parent if it is a container, or * rendered when attached if added to a gwt panel. * * @param target the element this component should be rendered into */ public void render(Element target) { render(target, -1); } /** * Renders the element. Typically, this method does not need to be called * directly. A component will rendered by its parent if it is a container, or * rendered when attached if added to a gwt panel. * * @param target the element this component should be rendered into * @param index the index within the container <b>before</b> which this * component will be inserted (defaults to appending to the end of * the container if value is -1) */ public void render(Element target, int index) { if (rendered || !fireEvent(Events.BeforeRender)) { return; } beforeRender(); if (plugins != null) { for (ComponentPlugin plugin : plugins) { plugin.init(this); } } rendered = true; createStyles(baseStyle); if (!setElementRender) { if (index == -1) { index = DOM.getChildCount(target); } onRender(target, index); } if (el == null) throw new RuntimeException(getClass().getName() + " must call setElement in onRender"); if (events != 0) { el().addEventsSunk(events); } if (id == null) { id = el.getId(); if (id == null || id.equals("")) { id = XDOM.getUniqueId(); } } getElement().setId(id); addStyleName(baseStyle); if (cls != null) { addStyleName(cls); cls = null; } if (title != null) { setTitle(title); } if (styles != null && !styles.equals("")) { el.applyStyles(styles); styles = null; } if (toolTipConfig != null) { setToolTip(toolTipConfig); } if (focused) { DeferredCommand.addCommand(new Command() { public void execute() { focus(); } }); } if (borders != Style.DEFAULT) { setBorders(borders == 1); } if (focusable && GXT.isWebKit) { focusEl = new El(createHiddenInput()); getElement().appendChild(focusEl.dom); } afterRender = true; afterRender(); if (hidden) { hide(); } if (disabled) { disable(); } initState(); fireEvent(Events.Render); } /** * Repaints the component if rendered. */ public void repaint() { if (rendered) { el().repaint(); } } /** * Saves the component's current state. */ public void saveState() { if (enableState && state != null) { ComponentEvent ce = createComponentEvent(null); ce.setState(state); if (fireEvent(Events.BeforeStateSave, ce)) { String sid = stateId != null ? stateId : getId(); StateManager.get().set(sid, state); fireEvent(Events.StateSave, ce); } } } /** * Adds or removes a border. The style name 'x-border' is added to the widget * to display a border. * * @param show <code>true</code> to display a border */ public void setBorders(boolean show) { borders = show ? 1 : 0; if (rendered) { fly(getStyleElement()).setBorders(show); } } /** * Sets the application defined property with the given name. * * @param key the name of the property * @param data the new value for the property */ public void setData(String key, Object data) { if (dataMap == null) dataMap = new FastMap<Object>(); dataMap.put(key, data); } // make public public void setElement(Element elem) { el = new El(elem); super.setElement(elem); if (!rendered) { setElementRender = true; render(null); } } /** * Convenience function for setting disabled/enabled by boolean. * * @param enabled the enabled state */ public void setEnabled(boolean enabled) { if (!enabled) { disable(); } else { enable(); } } /** * Sets whether the component's state is enabled (defaults to true). * * @param enable true to enable */ public void setEnableState(boolean enable) { this.enableState = enable; } /** * Overrides UIObject and does nothing. */ public void setHeight(String height) { } /** * Sets the components hide mode (default to HideMode.DISPLAY). * * @param hideMode the hide mode. */ public void setHideMode(HideMode hideMode) { this.hideMode = hideMode; } /** * Sets the component's id. * * @param id the new id */ public void setId(String id) { this.id = id; if (el != null) { DOM.setElementProperty(getElement(), "id", id); } } /** * Sets a style attribute. * * @param attr the attribute * @param value the attribute value */ public void setIntStyleAttribute(String attr, int value) { setStyleAttribute(attr, "" + value); } /** * Sets the component's item id. * * @param id the item id */ public void setItemId(String id) { this.itemId = id; } /** * Overrides UIObject and does nothing. */ public void setPixelSize(int width, int height) { } /** * Overrides UIObject and does nothing. */ public void setSize(String width, String height) { } /** * Sets a style attribute. * * @param attr the attribute * @param value the attribute value */ public void setStyleAttribute(String attr, String value) { if (rendered) { el().setStyleAttribute(attr, value); } else { styles += attr + ":" + value + ";"; } } @Override public void setStyleName(String style) { if (rendered) { super.setStyleName(style); } else { cls = style; } } @Override public void setTitle(String title) { this.title = title; if (rendered) { super.setTitle(title); } } /** * Sets the component's tool tip. * * @param text the text */ public void setToolTip(String text) { if (toolTipConfig == null) { toolTipConfig = new ToolTipConfig(); } toolTipConfig.setText(text); setToolTip(toolTipConfig); } /** * Sets the component's tool tip with the given config. * * @param config the tool tip config */ public void setToolTip(ToolTipConfig config) { this.toolTipConfig = config; if (rendered) { if (toolTip == null) { toolTip = new ToolTip(this, config); } else { toolTip.update(config); } } } /** * Convenience function to hide or show this component by boolean. * * @param visible the visible state */ public void setVisible(boolean visible) { if (visible) { show(); } else { hide(); } } /** * Overrides UIObject and does nothing. */ public void setWidth(String width) { } public void setZIndex(int zIndex) { el().setZIndex(zIndex); FocusFrame.get().sync(this); } /** * Show this component. Fires the <i>BeforeShow</i> event before the component * is made visible, then fires the <i>Show</i> event after the component is * visible. */ public void show() { if (fireEvent(Events.BeforeShow)) { hidden = false; if (rendered) { onShow(); } fireEvent(Events.Show); } } @Override public void sinkEvents(int eventBitsToAdd) { if (!rendered) { this.events = this.events | eventBitsToAdd; } else { super.sinkEvents(eventBitsToAdd); } } /** * Adds a swallow event. When enabled, any events of the given type whose * target is or is a child of the given element are swallowed. * * @param eventType the event type * @param element the target element * @param preventDefault true to prevent the default action * @return the swallow event config that can be used when removing a * swallowing event */ public SwallowEvent swallowEvent(EventType eventType, Element element, boolean preventDefault) { return swallowEvent(new SwallowEvent(eventType, element, preventDefault)); } public SwallowEvent swallowEvent(SwallowEvent e) { assert e.getEventType().isBrowserEvent() : "only browserevents are supported here"; if (swallowEvents == null) { swallowEvents = new ArrayList<SwallowEvent>(); } swallowEvents.add(e); return e; } @Override public String toString() { return el != null ? el.toString() : super.toString(); } /** * Unmasks the component. */ public void unmask() { mask = false; maskMessage = null; maskMessageStyleName = null; if (rendered) { el().unmask(); } } protected void addAttachable(ComponentAttachable a) { if (attachables == null) { attachables = new ArrayList<ComponentAttachable>(); } attachables.add(a); } /** * Adds a style to the given element on mouseover. The component must be * sinking mouse events for the over style to function. * * @param elem the over element * @param style the style to add */ protected void addStyleOnOver(Element elem, String style) { if (overElements == null) { overElements = new FastMap<String>(); } overElements.put(fly(elem).getId(), style); } /** * Called after the component has been rendered and is attached for the first * time. At this time, the component will be part of the DOM which is required * when retrieving location and offsets. */ protected void afterRender() { if (mask) { mask(maskMessage, maskMessageStyleName); } } protected void applyState(Map<String, Object> state) { } protected void assertAfterRender() { assert rendered : "Method must be called after the component is rendered"; } protected void assertPreRender() { assert !afterRender : "Method must be called before the component is rendered"; } /** * Called before the component has been rendered. * * <p/> * This method can be used to lazily alter this component pre-render */ protected void beforeRender() { } /** * Tries to remove focus from the component. Fires the <i>Blur</i> event. */ protected void blur() { if (rendered) { getFocusEl().blur(); } fireEvent(Events.Blur); } protected ComponentEvent createComponentEvent(Event event) { return new ComponentEvent(this, event); } protected Observable createObservable() { return new BaseObservable(); } protected void createStyles(String baseStyle) { } /** * Enables and disables the component's context menu. * * @param disable <code>true</code> to disable the context menu */ protected void disableContextMenu(boolean disable) { disableContextMenu = disable ? 1 : 0; if (isAttached()) { el.disableContextMenu(disable); } } @Override protected void doAttachChildren() { super.doAttachChildren(); if (attachables != null) { for (ComponentAttachable a : attachables) { a.doAttach(); } } } @Override protected void doDetachChildren() { super.doDetachChildren(); if (attachables != null) { for (ComponentAttachable a : attachables) { a.doDetach(); } } } protected void frame() { FocusFrame.get().frame(this); } /** * Returns the component's context menu. This method is marked protected, * subclasses can change access to public to expose the context menu. * * @return the context menu */ protected Menu getContextMenu() { return contextMenu; } protected El getFocusEl() { return focusEl == null ? el : focusEl; } protected LayoutData getLayoutData() { return layoutData; } protected Observable getObservable() { return observable; } /** * Returns the window resize delay. * * @return the delay */ protected int getWindowResizeDelay() { return windowResizeDelay; } protected void initState() { String sid = stateId != null ? stateId : getId(); Map<String, Object> st = StateManager.get().getMap(sid); if (st != null) { state = st; ComponentEvent ce = createComponentEvent(null); ce.setState(state); if (fireEvent(Events.BeforeStateRestore, ce)) { applyState(state); fireEvent(Events.StateRestore, ce); } } } /** * Returns true if browser resizing is monitored * * @return true if window resize monitoring is enabled */ protected boolean isMonitorWindowResize() { return monitorWindowResize; } @Override protected void onAttach() { // added to a gwt panel, not rendered if (!rendered) { // render and swap the proxy element String widgetIndex = dummy.getPropertyString("__widgetID"); Element parent = DOM.getParent(dummy); int index = DOM.getChildIndex(parent, dummy); parent.removeChild(dummy); render(parent, index); if (widgetIndex != null) { getElement().setPropertyInt("__widgetID", Integer.parseInt(widgetIndex)); } } if (disableTextSelection > 0) { el.disableTextSelection(disableTextSelection == 1); } if (disableContextMenu > 0) { el.disableContextMenu(disableContextMenu == 1); } super.onAttach(); if (monitorWindowResize) { if (windowResizeTask == null) { windowResizeTask = new DelayedTask(new Listener<BaseEvent>() { public void handleEvent(BaseEvent be) { onWindowResize(Window.getClientWidth(), Window.getClientHeight()); } }); } resizeHandler = Window.addResizeHandler(new ResizeHandler() { public void onResize(ResizeEvent event) { windowResizeTask.delay(windowResizeDelay); } }); } } @Override protected void onDetach() { hideToolTip(); FocusFrame.get().hide(this); if (disableTextSelection > 0) { el.disableTextSelection(false); } if (disableContextMenu > 0) { el.disableContextMenu(false); } if (resizeHandler != null) { resizeHandler.removeHandler(); resizeHandler = null; } super.onDetach(); fireEvent(Events.Detach); ComponentManager.get().unregister(this); } protected void onDisable() { addStyleName(disabledStyle); } protected void onEnable() { removeStyleName(disabledStyle); } @Override protected void onEnsureDebugId(String baseID) { setId(DEBUG_ID_PREFIX + baseID); } protected void onHide() { if (hideParent) { el().getParent().addStyleName("x-hide-" + hideMode.name().toLowerCase()); } else { addStyleName("x-hide-" + hideMode.name().toLowerCase()); } hideToolTip(); FocusFrame.get().hide(this); } protected void onHideContextMenu() { } @Override protected void onLoad() { super.onLoad(); fireEvent(Events.Attach); ComponentManager.get().register(this); } /** * Subclasses must override and ensure setElement is called for lazy rendered * components. * * @param target the target element * @param index the insert location */ protected void onRender(Element target, int index) { } protected void onRightClick(final ComponentEvent ce) { if (contextMenu != null) { ce.stopEvent(); final int x = ce.getClientX(); final int y = ce.getClientY(); if (fireEvent(Events.ContextMenu, ce)) { DeferredCommand.addCommand(new Command() { public void execute() { onShowContextMenu(x, y); } }); } } } protected void onShow() { if (hideParent) { el().getParent().removeStyleName("x-hide-" + hideMode.name().toLowerCase()); } else { removeStyleName("x-hide-" + hideMode.name().toLowerCase()); } FocusFrame.get().show(this); } protected void onShowContextMenu(int x, int y) { contextMenu.showAt(x, y); if (contextMenu.isVisible()) { contextMenu.addListener(Events.Hide, new Listener<ComponentEvent>() { public void handleEvent(ComponentEvent ce) { contextMenu.removeListener(Events.Hide, this); onHideContextMenu(); } }); } } protected void onWindowResize(int width, int height) { } protected ComponentEvent previewEvent(EventType type, ComponentEvent ce) { return ce; } protected void removeAttachagle(ComponentAttachable a) { if (attachables != null) { attachables.remove(a); } } protected void removeStyleOnOver(Element elem) { if (overElements != null) { overElements.remove(fly(elem).getId()); } } /** * Sets the component's context menu. * * @param menu the context menu */ protected void setContextMenu(Menu menu) { contextMenu = menu; disableContextMenu(true); sinkEvents(GXT.isSafari && GXT.isMac ? Event.ONMOUSEDOWN : Event.ONMOUSEUP); } protected void setEl(El el) { this.el = el; } protected void setElement(Element elem, Element parent, int index) { setElement(elem); DOM.insertChild(parent, elem, index); } protected void setFiresEvents(boolean firesEvents) { if (observable instanceof BaseObservable) { ((BaseObservable) observable).setFiresEvents(firesEvents); } } protected void setLayoutData(LayoutData data) { this.layoutData = data; } protected void setModel(ModelData model) { this.model = model; } /** * True to have onWindowResize executed when the browser window is resized * (default to false). * * You need to override onWindowResize to get your needed functionality * * @param monitorWindowResize true to monitor window resizing */ protected void setMonitorWindowResize(boolean monitorWindowResize) { this.monitorWindowResize = monitorWindowResize; } /** * Sets delay in milliseconds used to buffer window resizing (defaults to * 100). * * @param delay the delay */ protected void setWindowResizeDelay(int delay) { this.windowResizeDelay = delay; } protected void unframe() { FocusFrame.get().unframe(this); } private Element createHiddenInput() { Element input = DOM.createInputText(); input.setClassName("_focus"); com.google.gwt.dom.client.Style style = input.getStyle(); style.setProperty("opacity", "0"); style.setProperty("zIndex", "-1"); style.setProperty("overflow", "hidden"); style.setProperty("position", "absolute"); style.setPropertyPx("height", 0); style.setProperty("borderWidth", "0"); style.setPropertyPx("width", 0); return input; } }