com.sencha.gxt.widget.core.client.Component.java Source code

Java tutorial

Introduction

Here is the source code for com.sencha.gxt.widget.core.client.Component.java

Source

/**
 * Sencha GXT 4.0.0 - Sencha for GWT
 * Copyright (c) 2006-2015, Sencha Inc.
 *
 * licensing@sencha.com
 * http://www.sencha.com/products/gxt/license/
 *
 * ================================================================================
 * Open Source License
 * ================================================================================
 * This version of Sencha GXT is licensed under the terms of the Open Source GPL v3
 * license. You may use this license only if you are prepared to distribute and
 * share the source code of your application under the GPL v3 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * If you are NOT prepared to distribute and share the source code of your
 * application under the GPL v3 license, other commercial and oem licenses
 * are available for an alternate download of Sencha GXT.
 *
 * Please see the Sencha GXT Licensing page at:
 * http://www.sencha.com/products/gxt/license/
 *
 * For clarification or additional options, please contact:
 * licensing@sencha.com
 * ================================================================================
 *
 *
 * ================================================================================
 * Disclaimer
 * ================================================================================
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 * ================================================================================
 */
package com.sencha.gxt.widget.core.client;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.logical.shared.HasResizeHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.uibinder.client.UiChild;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.Style;
import com.sencha.gxt.core.client.Style.HideMode;
import com.sencha.gxt.core.client.Style.Side;
import com.sencha.gxt.core.client.dom.DomIdProvider;
import com.sencha.gxt.core.client.dom.Layer;
import com.sencha.gxt.core.client.dom.Layer.ShadowPosition;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.gestures.GestureRecognizer;
import com.sencha.gxt.core.client.gestures.HasGestureRecognizers;
import com.sencha.gxt.core.client.gestures.LongPressOrTapGestureRecognizer;
import com.sencha.gxt.core.client.gestures.PointerEventsSupport;
import com.sencha.gxt.core.client.gestures.TouchData;
import com.sencha.gxt.core.client.resources.ThemeStyles;
import com.sencha.gxt.core.client.util.DelayedTask;
import com.sencha.gxt.core.client.util.Point;
import com.sencha.gxt.core.client.util.Rectangle;
import com.sencha.gxt.core.client.util.Size;
import com.sencha.gxt.core.client.util.Util;
import com.sencha.gxt.core.shared.FastMap;
import com.sencha.gxt.core.shared.event.CancellableEvent;
import com.sencha.gxt.widget.core.client.event.BeforeHideEvent;
import com.sencha.gxt.widget.core.client.event.BeforeHideEvent.BeforeHideHandler;
import com.sencha.gxt.widget.core.client.event.BeforeHideEvent.HasBeforeHideHandlers;
import com.sencha.gxt.widget.core.client.event.BeforeShowContextMenuEvent;
import com.sencha.gxt.widget.core.client.event.BeforeShowContextMenuEvent.BeforeShowContextMenuHandler;
import com.sencha.gxt.widget.core.client.event.BeforeShowContextMenuEvent.HasBeforeShowContextMenuHandler;
import com.sencha.gxt.widget.core.client.event.BeforeShowEvent;
import com.sencha.gxt.widget.core.client.event.BeforeShowEvent.BeforeShowHandler;
import com.sencha.gxt.widget.core.client.event.BeforeShowEvent.HasBeforeShowHandlers;
import com.sencha.gxt.widget.core.client.event.BlurEvent;
import com.sencha.gxt.widget.core.client.event.BlurEvent.BlurHandler;
import com.sencha.gxt.widget.core.client.event.BlurEvent.HasBlurHandlers;
import com.sencha.gxt.widget.core.client.event.DisableEvent;
import com.sencha.gxt.widget.core.client.event.DisableEvent.DisableHandler;
import com.sencha.gxt.widget.core.client.event.DisableEvent.HasDisableHandlers;
import com.sencha.gxt.widget.core.client.event.EnableEvent;
import com.sencha.gxt.widget.core.client.event.EnableEvent.EnableHandler;
import com.sencha.gxt.widget.core.client.event.EnableEvent.HasEnableHandlers;
import com.sencha.gxt.widget.core.client.event.FocusEvent;
import com.sencha.gxt.widget.core.client.event.FocusEvent.FocusHandler;
import com.sencha.gxt.widget.core.client.event.FocusEvent.HasFocusHandlers;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent.HasHideHandlers;
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler;
import com.sencha.gxt.widget.core.client.event.MoveEvent;
import com.sencha.gxt.widget.core.client.event.MoveEvent.HasMoveHandlers;
import com.sencha.gxt.widget.core.client.event.MoveEvent.MoveHandler;
import com.sencha.gxt.widget.core.client.event.ShowContextMenuEvent;
import com.sencha.gxt.widget.core.client.event.ShowContextMenuEvent.HasShowContextMenuHandler;
import com.sencha.gxt.widget.core.client.event.ShowContextMenuEvent.ShowContextMenuHandler;
import com.sencha.gxt.widget.core.client.event.ShowEvent;
import com.sencha.gxt.widget.core.client.event.ShowEvent.HasShowHandlers;
import com.sencha.gxt.widget.core.client.event.ShowEvent.ShowHandler;
import com.sencha.gxt.widget.core.client.event.XEvent;
import com.sencha.gxt.widget.core.client.menu.Menu;
import com.sencha.gxt.widget.core.client.tips.ToolTip;
import com.sencha.gxt.widget.core.client.tips.ToolTipConfig;

/**
 * Base class for all GXT widgets.
 */
public class Component extends Widget implements HasFocusHandlers, HasBlurHandlers, HasBeforeHideHandlers,
        HasHideHandlers, HasBeforeShowHandlers, HasShowHandlers, HasEnableHandlers, HasDisableHandlers,
        HasBeforeShowContextMenuHandler, HasShowContextMenuHandler, HasMoveHandlers, HasResizeHandlers, HasItemId,
        HasFocusSupport, HasEnabled, HasGestureRecognizers {

    /**
     * True to adjust sizes for box model issues to ensure actual size matches set size.
     */
    protected boolean adjustSize = true;

    /**
     * True to cache size calculation (defaults to true) for better performance.
     */
    protected boolean cacheSizes = true;

    /**
     * True if the widget is disabled. Read only.
     */
    protected boolean disabled;

    /**
     * The style used when a widget is disabled (defaults to {@code ThemeStyles.getStyle().disabled()}).
     */
    protected String disabledStyle = ThemeStyles.get().style().disabled();

    protected boolean allowTextSelection = true;

    /**
     * Set this to true if you have sizing issues in initial collapsed or hidden items. It defaults to false for
     * performance reasons. You should not set this to true for all components. If this is enabled than components are
     * made visible for the browser during a call to setSize if they were hidden or collapsed. The user wont see this
     * change. In the end of setSize the status of the widget is reverted again to the normal state. (defaults to false)
     */
    protected boolean ensureVisibilityOnSizing;

    /**
     * True if this widget is hidden. Read only.
     */
    protected boolean hidden;

    /**
     * The size of the widget that last time it was changed.
     */
    protected Size lastSize;
    protected String height;
    protected Layer layer;
    protected int left = Style.DEFAULT, top = Style.DEFAULT;
    protected boolean mask;
    protected String maskMessage;
    protected boolean monitorWindowResize;
    protected int pageX = Style.DEFAULT, pageY = Style.DEFAULT;
    protected HandlerRegistration resizeHandler;

    /**
     * True to enable a shim which uses a transparent iframe to stop content from bleeding through.
     */
    protected boolean shim;
    protected ToolTip toolTip;
    protected ToolTipConfig toolTipConfig;
    protected boolean disableContextMenu = false;

    protected String width;
    protected int windowResizeDelay = !GWT.isScript() ? 100 : 0;
    protected DelayedTask windowResizeTask;
    protected int tabIndex;
    private Menu contextMenu;
    private Map<String, Object> dataMap;
    private boolean deferHeight;
    private boolean disableEvents;
    private FocusManagerSupport focusManagerSupport;
    private HideMode hideMode = HideMode.DISPLAY;
    private String itemId;
    private Map<String, String> overElements;
    private List<GestureRecognizer> gestureRecognizers;
    private GestureRecognizer contextMenuGestureRecognizer;
    private boolean shadow;
    private ShadowPosition shadowPosition = ShadowPosition.SIDES;
    private boolean stateful;
    private String stateId;

    protected Component() {

    }

    public HandlerRegistration addBeforeHideHandler(BeforeHideHandler handler) {
        return addHandler(handler, BeforeHideEvent.getType());
    }

    @Override
    public HandlerRegistration addBeforeShowContextMenuHandler(BeforeShowContextMenuHandler handler) {
        return addHandler(handler, BeforeShowContextMenuEvent.getType());
    }

    @Override
    public HandlerRegistration addBeforeShowHandler(BeforeShowHandler handler) {
        return addHandler(handler, BeforeShowEvent.getType());
    }

    @Override
    public HandlerRegistration addBlurHandler(BlurHandler handler) {
        return addHandler(handler, BlurEvent.getType());
    }

    @Override
    public HandlerRegistration addDisableHandler(DisableHandler handler) {
        return addHandler(handler, DisableEvent.getType());
    }

    @Override
    public HandlerRegistration addEnableHandler(EnableHandler handler) {
        return addHandler(handler, EnableEvent.getType());
    }

    @Override
    public HandlerRegistration addFocusHandler(FocusHandler handler) {
        return addHandler(handler, FocusEvent.getType());
    }

    @Override
    public HandlerRegistration addHideHandler(HideHandler handler) {
        return addHandler(handler, HideEvent.getType());
    }

    @Override
    public HandlerRegistration addMoveHandler(MoveHandler handler) {
        return addHandler(handler, MoveEvent.getType());
    }

    @Override
    public HandlerRegistration addResizeHandler(ResizeHandler handler) {
        return addHandler(handler, ResizeEvent.getType());
    }

    @Override
    public HandlerRegistration addShowContextMenuHandler(ShowContextMenuHandler handler) {
        return addHandler(handler, ShowContextMenuEvent.getType());
    }

    @Override
    public HandlerRegistration addShowHandler(ShowHandler handler) {
        return addHandler(handler, ShowEvent.getType());
    }

    @Override
    public void addGestureRecognizer(GestureRecognizer gestureRecognizer) {
        if (gestureRecognizers == null) {
            gestureRecognizers = new ArrayList<GestureRecognizer>();
            sinkEvents(Event.TOUCHEVENTS);
        }
        gestureRecognizers.add(gestureRecognizer);
        gestureRecognizer.setDelegate(this);
    }

    @Override
    public GestureRecognizer getGestureRecognizer(int index) {
        if (gestureRecognizers != null) {
            return gestureRecognizers.get(index);
        }
        return null;
    }

    @Override
    public int getGestureRecognizerCount() {
        return gestureRecognizers == null ? 0 : gestureRecognizers.size();
    }

    /**
     * Adds a style to the given element on mouseover. The widget must be sinking mouse events for the over style to
     * function.
     *
     * @param elem the over element
     * @param style the style to add
     */
    public void addStyleOnOver(Element elem, String style) {
        if (overElements == null) {
            overElements = new FastMap<String>();
        }
        overElements.put(elem.getId(), style);
    }

    /**
     * Clears the size cache (the size of the widget the last time it was changed).
     */
    public void clearSizeCache() {
        lastSize = null;
    }

    /**
     * Disable this widget.
     */
    public void disable() {
        onDisable();
        disabled = true;
        fireEvent(new DisableEvent());
    }

    /**
     * True to disable event processing.
     */
    public void disableEvents() {
        disableEvents = true;
    }

    /**
     * Enable this widget.
     */
    public void enable() {
        onEnable();
        disabled = false;
        fireEvent(new EnableEvent());
    }

    /**
     * True to enable event processing.
     */
    public void enableEvents() {
        disableEvents = false;
    }

    @Override
    public void fireEvent(GwtEvent<?> event) {
        if (disableEvents)
            return;
        super.fireEvent(event);
    }

    /**
     * Try to focus this widget.
     */
    public void focus() {
        FocusImpl.getFocusImplForWidget().focus(getFocusEl());
    }

    /**
     * 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);
    }

    /**
     * Gets a handle to the object's underlying DOM element. This method should not be overridden. It is non-final solely
     * to support legacy code that depends upon overriding it. If it is overridden, the subclass implementation must not
     * return a different element than was previously set using {@link #setElement(com.google.gwt.dom.client.Element)}.
     *
     * @return the object's browser element
     */
    @Override
    public XElement getElement() {
        return super.getElement().<XElement>cast();
    }

    /**
     * Returns the focus manager support configuration. Only applicable when the focus manager has been enabled.
     *
     * @return the focus manager configuration
     */
    public FocusManagerSupport getFocusSupport() {
        if (focusManagerSupport == null) {
            focusManagerSupport = new FocusManagerSupport(this);
        }
        return focusManagerSupport;
    }

    /**
     * Returns the widget's hide mode.
     *
     * @return the hide mode
     */
    public HideMode getHideMode() {
        return hideMode;
    }

    /**
     * Sets the components hide mode (defaults to HideMode.DISPLAY).
     *
     * @param hideMode the hide mode.
     */
    public void setHideMode(HideMode hideMode) {
        this.hideMode = hideMode;
    }

    /**
     * Returns the widget's id.
     *
     * @return the widget id
     */
    public String getId() {
        String id = getElement().getId();
        if (id == null || "".equals(id)) {
            id = DomIdProvider.generateId(this);
            getElement().setId(id);
        }
        return id;
    }

    /**
     * Sets the component's id.
     *
     * @param id the id
     */
    public void setId(String id) {
        getElement().setId(id);
    }

    /**
     * Returns the item id of this widget. Unlike the widget's id, the item id does not have to be unique.
     *
     * @return the widget's item id
     */
    @Override
    public String getItemId() {
        return itemId != null ? itemId : getId();
    }

    /**
     * Sets the widget's item id. Unlike a widget's id, the widget's item id is not tied to id attribute of the widget's
     * root element. As such, the item id does not have to be unique.
     *
     * @param id the item id
     */
    @Override
    public void setItemId(String id) {
        this.itemId = id;
    }

    /**
     * Returns the widget's height.
     *
     * @param content true to get the height minus borders and padding
     * @return the element's height
     */
    public int getOffsetHeight(boolean content) {
        int h = getOffsetHeight();
        if (content) {
            h -= getElement().getFrameWidth(Side.TOP, Side.BOTTOM);
        }
        return Math.max(0, h);
    }

    /**
     * Returns the element's width.
     *
     * @param content true to get the width minus borders and padding
     * @return the width
     */
    public int getOffsetWidth(boolean content) {
        int w = getOffsetWidth();
        if (content) {
            w -= getElement().getFrameWidth(Side.LEFT, Side.RIGHT);
        }
        return Math.max(0, w);
    }

    /**
     * Returns true if the shadow is enabled.
     *
     * @return the shadow the shadow state
     */
    public boolean getShadow() {
        return shadow;
    }

    /**
     * True to enable a shadow that will be displayed behind the widget (defaults to false, pre-render).
     *
     * @param shadow true to enable the shadow
     */
    public void setShadow(boolean shadow) {
        assertPreRender();
        this.shadow = shadow;
    }

    /**
     * Returns the shadow position.
     *
     * @return the shadow position
     */
    public ShadowPosition getShadowPosition() {
        return shadowPosition;
    }

    /**
     * Sets the shadow position (defaults to SIDES, pre-render).
     *
     * @param shadowPosition the position
     */
    public void setShadowPosition(ShadowPosition shadowPosition) {
        assertPreRender();
        this.shadowPosition = shadowPosition;
    }

    /**
     * Returns the widget's state id. If a state id is specified, it is used as the key when saving and retrieving the
     * widget's state.
     *
     * @return the state id
     */
    public String getStateId() {
        if (stateId == null) {
            stateId = getId();
        }
        return stateId;
    }

    /**
     * Sets the widget's state id which is a unique id for this widget to use for state management purposes (defaults to
     * the widget id if one was set, otherwise null if the widget is using a generated id).
     *
     * @param stateId the state id
     */
    public void setStateId(String stateId) {
        this.stateId = stateId;
    }

    /**
     * Returns the current tabIndex of the component. By default this is the tabIndex of the root element, but some
     * subclasses may modify this behavior.
     *
     * @return the tabIndex of the component
     */
    public int getTabIndex() {
        return tabIndex;
    }

    /**
     * Sets the component's tab index. Subclasses may override this to set the tab index on a specific element for better
     * focus behavior - they are also responsible for setting the tabIndex field.
     *
     * @param tabIndex the tab index
     */
    public void setTabIndex(int tabIndex) {
        this.tabIndex = tabIndex;
        getElement().setTabIndex(tabIndex);
    }

    /**
     * Returns the widget's tool tip.
     *
     * @return the tool tip
     */
    public ToolTip getToolTip() {
        if (toolTip == null && toolTipConfig != null) {
            toolTip = new ToolTip(this, toolTipConfig);
        }
        return toolTip;
    }

    /**
     * Hide this widget.
     */
    public void hide() {
        if (fireCancellableEvent(new BeforeHideEvent())) {
            hidden = true;
            onHide();
            notifyHide();
            fireEvent(new HideEvent());
        }
    }

    /**
     * Hides the widget's tool tip (if one exists).
     */
    public void hideToolTip() {
        if (toolTip != null) {
            toolTip.hide();
        }
    }

    /**
     * Returns the enable text selection state.
     *
     * @return true if enable, false if disabled
     */
    public boolean isAllowTextSelection() {
        return allowTextSelection;
    }

    /**
     * Enables and disables text selection for the widget.
     *
     * @param enable true to enable, false to disable
     */
    public void setAllowTextSelection(boolean enable) {
        allowTextSelection = enable;
        if (isAttached()) {
            getElement().disableTextSelection(!enable);
        }
    }

    /**
     * Returns the auto height state.
     *
     * @return the auto height state
     */
    public boolean isAutoHeight() {
        return height == null;
    }

    /**
     * Returns the auto width state.
     *
     * @return true of auto width
     */
    public boolean isAutoWidth() {
        return width == null;
    }

    /**
     * Returns true if the height is being deferred
     *
     * @return the defer height state
     */
    public boolean isDeferHeight() {
        return deferHeight;
    }

    /**
     * True to defer height calculations to an external widget, false to allow this widget to set its own height (defaults
     * to false).
     *
     * @param deferHeight true to defer height
     */
    public void setDeferHeight(boolean deferHeight) {
        this.deferHeight = deferHeight;
    }

    /**
     * Returns <code>true</code> if the widget is enabled.
     *
     * @return the enabled state
     */
    public boolean isEnabled() {
        return !disabled;
    }

    /**
     * Convenience function for setting disabled/enabled by boolean.
     *
     * @param enabled the enabled state
     */
    public void setEnabled(boolean enabled) {
        if (!enabled) {
            disable();
        } else {
            enable();
        }
    }

    /**
     * Returns true if the component has been rendered.
     *
     * @return true if rendered
     */
    public boolean isRendered() {
        return super.isOrWasAttached();
    }

    /**
     * Returns true if the widget is saving and restore it's state.
     *
     * @return true if stateful
     */
    public boolean isStateful() {
        return stateful;
    }

    /**
     * A flag which specifies if the component is stateful (defaults to false). The widget must have either a
     * {@link #setStateId(String)} or {@link #setId(String)} assigned for state to be managed. Auto-generated ids are not
     * guaranteed to be stable across page loads and cannot be relied upon to save and restore the same state for a
     * widget.
     *
     * @param stateful true to enable state
     */
    public void setStateful(boolean stateful) {
        this.stateful = stateful;
    }

    @Override
    public boolean isVisible() {
        return isVisible(false);
    }

    /**
     * Convenience function to hide or show this widget by boolean.
     *
     * @param visible the visible state
     */
    @Override
    public void setVisible(boolean visible) {
        if (visible) {
            show();
        } else {
            hide();
        }
    }

    /**
     * Returns <code>true</code> if the widget is visible.
     *
     * @param deep true to search up the widget hierarchy
     * @return true if the widget is visible
     */
    public boolean isVisible(boolean deep) {
        Widget w = getParent();
        if (deep && w != null) {
            if (w instanceof Component) {
                Component c = (Component) w;
                return isAttached() && !hidden && getElement().isVisible(false) && c.isVisible(deep);
            } else {
                return isAttached() && !hidden && w.isVisible() && getElement().isVisible(deep);
            }
        } else {
            return isAttached() && !hidden && getElement().isVisible(deep);
        }
    }

    /**
     * Puts a mask over this widget to disable user interaction.
     */
    public void mask() {
        mask(null);
    }

    /**
     * Puts a mask over this widget to disable user interaction.
     *
     * @param message a message to display in the mask
     */
    public void mask(String message) {
        mask = true;
        maskMessage = message;
        getElement().mask(message);
    }

    @Override
    public void onBrowserEvent(Event event) {
        switch (event.getTypeInt()) {
        case Event.ONFOCUS:
            onFocus(event);
            break;
        case Event.ONBLUR:
            onBlur(event);
            break;
        case Event.ONCONTEXTMENU:
            if (disableContextMenu) {
                event.preventDefault();
            }
            onRightClick(event);
            break;
        case Event.ONTOUCHSTART:
        case Event.ONTOUCHMOVE:
        case Event.ONTOUCHCANCEL:
        case Event.ONTOUCHEND:
            onTouch(event);
            break;
        }
        if (PointerEventsSupport.impl.isSupported() && PointerEventsSupport.impl.isPointerEvent(event)) {
            onTouch(event);
        }

        int type = event.getTypeInt();
        // specialized support for mouse overs
        if (overElements != null && (type == Event.ONMOUSEOVER || type == Event.ONMOUSEOUT)) {
            XElement target = event.getEventTarget().cast();
            if (target != null) {
                String style = overElements.get(target.getId());
                if (style != null) {
                    target.setClassName(style, type == Event.ONMOUSEOVER);
                }
            }
        }

        // we are not calling super so must fire dom events
        DomEvent.fireNativeEvent(event, this, this.getElement());
    }

    /**
     * Removes the components tooltip (if one exists).
     */
    public void removeToolTip() {
        if (toolTip != null) {
            toolTip.initTarget(null);
            toolTip = null;
            toolTipConfig = null;
        }
    }

    /**
     * Adds or removes a border.
     *
     * @param show <code>true</code> to display a border
     */
    public void setBorders(boolean show) {
        XElement.as(getStyleElement()).setBorders(show);
    }

    /**
     * Sets the widget's size.
     *
     * @param x the x coordinate
     * @param y the y coordinate
     * @param width the width
     * @param height the height
     */
    public void setBounds(int x, int y, int width, int height) {
        setPagePosition(x, y);
        setPixelSize(width, height);
    }

    /**
     * Sets the widget's size.
     *
     * @param bounds the update box
     */
    public void setBounds(Rectangle bounds) {
        setBounds(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
    }

    /**
     * Sets the widget's context menu.
     *
     * @param menu the context menu
     */
    @UiChild(tagname = "contextmenu", limit = 1)
    public void setContextMenu(Menu menu) {
        contextMenu = menu;
        disableContextMenu(true);

        if (contextMenuGestureRecognizer == null) {
            contextMenuGestureRecognizer = new LongPressOrTapGestureRecognizer() {
                @Override
                protected void onLongPress(TouchData touchData) {
                    super.onLongPress(touchData);
                    onRightClick((Event) touchData.getLastNativeEvent());
                }

                @Override
                public boolean handleEnd(NativeEvent endEvent) {
                    // onRightClick does preventDefault and stopPropagation
                    cancel();
                    return super.handleEnd(endEvent);
                }
            };
            addGestureRecognizer(contextMenuGestureRecognizer);
        }
    }

    /**
     * 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);
    }

    /**
     * Sets the widget's height. This method fires the <i>Resize</i> event. element.
     *
     * @param height the new height
     */
    public void setHeight(int height) {
        setPixelSize(Integer.MIN_VALUE, height);
    }

    /**
     * Sets the height of the widget. This method fires the <i>Resize</i> event. element.
     *
     * @param height the new height to set
     */
    public void setHeight(String height) {
        setSize(Style.UNDEFINED, height);
    }

    /**
     * Sets the page XY position of the widget. To set the left and top instead, use {@link #setPosition}.
     *
     * @param x the x coordinate
     * @param y the y coordinate
     */
    public void setPagePosition(int x, int y) {
        if (x != Style.DEFAULT) {
            pageX = x;
        }
        if (y != Style.DEFAULT) {
            pageY = y;
        }

        if (!isAttached()) {
            return;
        }
        Point p = getPositionEl().translatePoints(new Point(x, y));
        setPosition(p.getX(), p.getY());
    }

    /**
     * Sets the component's size. Unlike GWT widget's, when setting sizes, the component's actual size will match exactly
     * the size specified independent of borders and padding.
     *
     * @param width new width, in pixels
     * @param height height, in pixels
     */
    @Override
    public void setPixelSize(int width, int height) {
        int w, h;
        if (width == -1) {
            w = width;
            this.width = null;
        } else if (width != Integer.MIN_VALUE) {
            w = width;
            this.width = width + "px";
        } else {
            w = Util.parseInt(this.width, -1);
        }

        if (height == -1) {
            this.height = null;
            h = height;
        } else if (height != Integer.MIN_VALUE) {
            h = height;
            this.height = height + "px";
        } else {
            h = Util.parseInt(this.height, -1);
        }

        if (!isAttached()) {
            return;
        }

        Size size = new Size(w, h);
        if (cacheSizes && lastSize != null && lastSize.equals(size)) {
            return;
        }

        List<FastMap<Object>> list = makeVisible();

        lastSize = size;

        Size ads = adjustSize(size);

        int aw = ads.getWidth();
        int ah = ads.getHeight();

        if (width != -1 && height != -1 && width != Integer.MIN_VALUE && height != Integer.MIN_VALUE
                && !deferHeight) {
            getElement().setSize(aw, ah, adjustSize);
        } else if (width != -1 && width != Integer.MIN_VALUE) {
            getElement().setWidth(aw, adjustSize);
            if (height != Integer.MIN_VALUE) {
                getElement().getStyle().clearHeight();
            }
        } else if (height != -1 && !deferHeight) {
            getElement().setHeight(ah, adjustSize);
            if (width != Integer.MIN_VALUE) {
                getElement().getStyle().clearWidth();
            }
        }

        restoreVisible(list);

        onResize(aw, ah);

        Scheduler.get().scheduleFinally(new ScheduledCommand() {

            @Override
            public void execute() {
                sync(true);
            }
        });

        ResizeEvent.fire(this, aw, ah);
    }

    /**
     * Sets the left and top of the widget. To set the page XY position instead, use {@link #setPagePosition}.
     *
     * @param left the new left
     * @param top the new top
     */
    public void setPosition(int left, int top) {
        this.left = left;
        this.top = top;

        if (!isAttached()) {
            return;
        }

        getElement().makePositionable();

        Point p = new Point(left, top);

        p = adjustPosition(p);
        int ax = p.getX(), ay = p.getY();

        XElement pel = getPositionEl();

        if (ax != Style.DEFAULT || ay != Style.DEFAULT) {
            if (ax != Style.DEFAULT) {
                pel.setLeft(ax);
            }
            if (ay != Style.DEFAULT) {
                pel.setTop(ay);
            }

            onPosition(ax, ay);

            fireEvent(new MoveEvent(ax, ay));
        }
    }

    /**
     * Sets the width and height of the widget. This method fires the <i>Resize</i> event.
     *
     * @param width the new width to set
     * @param height the new height to set
     */
    @Override
    public void setSize(String width, String height) {
        if (width != null && !Style.UNDEFINED.equals(width)) {
            width = XElement.addUnits(width, "px");
        }

        if (height != null && !Style.UNDEFINED.equals(height)) {
            height = XElement.addUnits(height, "px");
        }

        if ((height == null && width != null && width.endsWith("px"))
                || (width == null && height != null && height.endsWith("px"))
                || (width != null && height != null && width.endsWith("px") && height.endsWith("px"))) {
            int w, h;
            if (width == null) {
                w = -1;
            } else if (Style.UNDEFINED.equals(width)) {
                w = Integer.MIN_VALUE;
            } else {
                w = Util.parseInt(width, -1);
            }
            if (height == null) {
                h = -1;
            } else if (Style.UNDEFINED.equals(height)) {
                h = Integer.MIN_VALUE;
            } else {
                h = Util.parseInt(height, -1);
            }
            setPixelSize(w, h);
            return;
        }

        if (width == null) {
            this.width = null;
        } else if (width.equals(Style.UNDEFINED)) {
            width = this.width;
        } else {
            this.width = width;
        }

        if (height == null) {
            this.height = null;
        } else if (height.equals(Style.UNDEFINED)) {
            height = this.height;
        } else {
            this.height = height;
        }

        if (!isAttached()) {
            return;
        }

        if (width != null) {
            getElement().setWidth(width);
        } else {
            getElement().getStyle().clearWidth();
        }

        if (height != null) {
            if (!deferHeight) {
                getElement().setHeight(height);
            }
        } else {
            getElement().getStyle().clearHeight();
        }

        int w = -1;
        int h = -1;

        List<FastMap<Object>> list = makeVisible();

        if (width == null) {
            w = -1;
        } else if (width.indexOf("px") != -1) {
            w = Util.parseInt(width, -1);
        } else {
            w = getOffsetWidth();
        }
        if (height == null) {
            h = -1;
        } else if (height.indexOf("px") != -1) {
            h = Util.parseInt(height, -1);
        } else {
            h = getOffsetHeight();
        }

        Size size = new Size(w, h);
        if (cacheSizes && lastSize != null && lastSize.equals(size)) {
            return;
        }

        lastSize = size;

        onResize(w, h);

        sync(true);

        restoreVisible(list);

        ResizeEvent.fire(this, w, h);
    }

    /**
     * Sets the widget's tool tip.
     *
     * @param html the tip text or html
     */
    public void setToolTip(SafeHtml html) {
        if (toolTipConfig == null) {
            toolTipConfig = new ToolTipConfig();
        }
        toolTipConfig.setBody(html);
        setToolTipConfig(toolTipConfig);
    }

    /**
     * Sets the widget's tool tip.
     * 
     * @param text the tip text
     */
    public void setToolTip(String text) {
        if (toolTipConfig == null) {
            toolTipConfig = new ToolTipConfig();
        }
        toolTipConfig.setBody(text);
        setToolTipConfig(toolTipConfig);
    }

    /**
     * Sets the widget's tool tip with the given config.
     *
     * @param config the tool tip config
     */
    public void setToolTipConfig(ToolTipConfig config) {
        this.toolTipConfig = config;
        if (config != null) {
            if (toolTip == null) {
                toolTip = new ToolTip(this, config);
            } else {
                toolTip.update(config);
            }
        } else if (config == null) {
            removeToolTip();
        }
    }

    /**
     * Sets the width of the widget. This method fires the <i>Resize</i> event.
     *
     * @param width the new width to set
     */
    public void setWidth(int width) {
        setPixelSize(width, Integer.MIN_VALUE);
    }

    /**
     * Sets the width of the widget. This method fires the <i>Resize</i> event.
     *
     * @param width the new width to set
     */
    public void setWidth(String width) {
        setSize(width, Style.UNDEFINED);
    }

    /**
     * Show this widget.
     */
    public void show() {
        if (fireCancellableEvent(new BeforeShowEvent())) {
            hidden = false;
            onShow();
            notifyShow();
            fireEvent(new ShowEvent());
        }
    }

    /**
     * Syncs the layer of the widget.
     *
     * @param show true to show the layer
     */
    public void sync(boolean show) {
        if (layer != null) {
            layer.sync(show);
        }
    }

    /**
     * Clears the size cache and resets to the last known size.
     */
    public void syncSize() {
        Size oldSize = lastSize;
        lastSize = null;
        if (isAttached() && oldSize != null) {
            setPixelSize(oldSize.getWidth(), oldSize.getHeight());
        }
    }

    /**
     * Unmasks the widget.
     */
    public void unmask() {
        mask = false;
        maskMessage = null;
        getElement().unmask();
    }

    /**
     * Adds a dependent style name to a child element.
     *
     * @param element the element
     * @param style the style name
     */
    protected void addStyleDependentName(Element element, String style) {
        element.addClassName(getStylePrimaryName() + "-" + style);
    }

    protected Point adjustPosition(Point point) {
        return point;
    }

    protected Size adjustSize(Size size) {
        return size;
    }

    protected void applyState(Map<String, Object> state) {

    }

    protected void assertAfterRender() {
        assert isOrWasAttached() : "Method must be called after the widget is rendered";
    }

    protected void assertPreRender() {
        assert !isOrWasAttached() : "Method must be called before the widget is rendered";
    }

    /**
     * Tries to remove focus from the widget.
     */
    protected void blur() {
        FocusImpl.getFocusImplForWidget().blur(getFocusEl());
    }

    /**
     * Enables and disables the widget's context menu.
     *
     * @param disable <code>true</code> to disable the context menu
     */
    protected void disableContextMenu(boolean disable) {
        disableContextMenu = disable;
        if (disable) {
            sinkEvents(Event.ONCONTEXTMENU);
        }
    }

    protected boolean fireCancellableEvent(GwtEvent<?> event) {
        if (disableEvents)
            return true;
        fireEvent(event);
        if (event instanceof CancellableEvent) {
            return !((CancellableEvent) event).isCancelled();
        }
        return true;
    }

    protected XElement getFocusEl() {
        return getElement();
    }

    /**
     * Returns the element to be used when positioning the widget. Subclasses may override as needed. Default method
     * returns the widget's root element.
     *
     * @return the position element
     */
    protected XElement getPositionEl() {
        return getElement();
    }

    protected void hideShadow() {
        if (layer != null) {
            layer.hideShadow();
        }
    }

    /**
     * Returns true if browser resizing is monitored
     *
     * @return true if window resize monitoring is enabled
     */
    protected boolean isMonitorWindowResize() {
        return monitorWindowResize;
    }

    /**
     * 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;
    }

    protected void notifyHide() {
    }

    protected void notifyShow() {
    }

    /**
     * Called immediately after the first time the widget becomes attached to the browser's document only the first time.
     */
    protected void onAfterFirstAttach() {
        if (shadow || (shim && GXT.isUseShims())) {
            layer = new Layer(getElement());
            if (shadow) {
                layer.enableShadow();
                layer.setShadowPosition(shadowPosition);
            }
            if (shim && GXT.isUseShims()) {
                layer.enableShim();
            }
        }

        if (left != Style.DEFAULT || top != Style.DEFAULT) {
            setPosition(left, top);
        }
        if (pageX != Style.DEFAULT || pageY != Style.DEFAULT) {
            setPagePosition(pageX, pageY);
        }
    }

    @Override
    protected void onAttach() {
        boolean isOrWasAttached = isOrWasAttached();
        super.onAttach();

        if (!isOrWasAttached) {
            onAfterFirstAttach();
        }

        if (width != null || height != null) {
            setSize(width, height);
        }

    }

    protected void onBlur(Event event) {
        fireEvent(new BlurEvent());
    }

    @Override
    protected void onDetach() {
        super.onDetach();

        hideToolTip();

        if (layer != null) {
            layer.hideUnders();
        }
    }

    protected void onDisable() {
        if (disabledStyle != null) {
            addStyleName(disabledStyle);
            addStyleName(ThemeStyles.get().style().disabled());
        }
        if (toolTip != null) {
            toolTip.disable();
        }
    }

    protected void onEnable() {
        if (disabledStyle != null) {
            removeStyleName(disabledStyle);
            removeStyleName(ThemeStyles.get().style().disabled());
        }
        if (toolTip != null) {
            toolTip.enable();
        }
    }

    protected void onFocus(Event event) {
        fireEvent(new FocusEvent());
    }

    protected void onHide() {
        addStyleName(hideMode.value());
        if (layer != null) {
            layer.hideUnders();
        }
        hideToolTip();
    }

    protected void onHideContextMenu(HideEvent event) {

    }

    @Override
    protected void onLoad() {
        super.onLoad();

        if (!allowTextSelection) {
            setAllowTextSelection(false);
        }

        if (monitorWindowResize) {
            if (windowResizeTask == null) {
                windowResizeTask = new DelayedTask() {
                    @Override
                    public void onExecute() {
                        onWindowResize(Window.getClientWidth(), Window.getClientHeight());
                    }
                };
            }
            // window resizing in some cases when initially showing positioned widgets
            if (GXT.isIE8()) {
                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                    @Override
                    public void execute() {
                        resizeHandler = Window.addResizeHandler(new ResizeHandler() {
                            public void onResize(ResizeEvent event) {
                                windowResizeTask.delay(windowResizeDelay);
                            }
                        });
                    }
                });
            } else {
                resizeHandler = Window.addResizeHandler(new ResizeHandler() {
                    public void onResize(ResizeEvent event) {
                        windowResizeTask.delay(windowResizeDelay);
                    }
                });
            }
        }
    }

    /**
     * Called after the widget is moved, this method is empty by default but can be implemented by any subclass that needs
     * to perform custom logic after a move occurs.
     *
     * @param x the new x position
     * @param y the new y position
     */
    protected void onPosition(int x, int y) {
    }

    /**
     * Called after the widget is resized, this method is empty by default but can be implemented by any subclass that
     * needs to perform custom logic after a resize occurs.
     *
     * @param width the width
     * @param height the height
     */
    protected void onResize(int width, int height) {
        if (mask) {
            mask(maskMessage);
        }
    }

    protected void onRightClick(Event event) {
        if (contextMenu != null && fireCancellableEvent(new BeforeShowContextMenuEvent(contextMenu))) {
            event.preventDefault();
            event.stopPropagation();

            Point point = event.<XEvent>cast().getXY();
            final int x = point.getX();
            final int y = point.getY();
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {

                @Override
                public void execute() {
                    onShowContextMenu(x, y);
                }
            });
        }
    }

    protected void onShow() {
        removeStyleName(hideMode.value());
        sync(true);
        Scheduler.get().scheduleFinally(new ScheduledCommand() {
            @Override
            public void execute() {
                sync(true);
            }
        });
    }

    protected void onShowContextMenu(int clientX, int clientY) {
        contextMenu.showAt(clientX, clientY);
        if (contextMenu.isVisible()) {
            fireEvent(new ShowContextMenuEvent(contextMenu));
            contextMenu.addHideHandler(new HideHandler() {

                @Override
                public void onHide(HideEvent event) {
                    ComponentHelper.removeHandler(contextMenu, HideEvent.getType(), this);
                    onHideContextMenu(event);
                }
            });
        }
    }

    protected void onTouch(Event event) {
        for (int i = 0, len = getGestureRecognizerCount(); i < len; i++) {
            getGestureRecognizer(i).handle(event);
        }
    }

    @Override
    protected void onUnload() {
        super.onUnload();

        if (!allowTextSelection) {
            getElement().disableTextSelection(false);
        }

        if (resizeHandler != null) {
            resizeHandler.removeHandler();
            resizeHandler = null;
        }
    }

    protected void onWindowResize(int width, int height) {
    }

    /**
     * Removes a dependent style name from a child element.
     *
     * @param element the element
     * @param style the style name
     */
    protected void removeStyleDependentName(Element element, String style) {
        element.removeClassName(getStylePrimaryName() + "-" + style);
    }

    protected void removeStyleOnOver(Element elem) {
        if (overElements != null) {
            overElements.remove(elem.getId());
        }
    }

    /**
     * Adds or removes a dependent style name to a child element.
     *
     * @param element the element
     * @param style the style name
     * @param add true to add, otherwise remove
     */
    protected void setStyleDependentName(Element element, String style, boolean add) {
        element.<XElement>cast().setClassName(getStylePrimaryName() + "-" + style, add);
    }

    private List<FastMap<Object>> makeVisible() {
        if (ensureVisibilityOnSizing) {
            return getElement().ensureVisible();
        }
        return null;
    }

    private void restoreVisible(List<FastMap<Object>> list) {
        if (ensureVisibilityOnSizing && list != null) {
            getElement().restoreVisible(list);
        }
    }
}