com.extjs.gxt.ui.client.widget.tips.ToolTip.java Source code

Java tutorial

Introduction

Here is the source code for com.extjs.gxt.ui.client.widget.tips.ToolTip.java

Source

/*
 * Sencha GXT 2.3.1 - Sencha for GWT
 * Copyright(c) 2007-2013, Sencha, Inc.
 * licensing@sencha.com
 * 
 * http://www.sencha.com/products/gxt/license/
 */
package com.extjs.gxt.ui.client.widget.tips;

import java.util.Date;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.core.XDOM;
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.util.*;
import com.extjs.gxt.ui.client.widget.Component;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;

/**
 * A standard tooltip implementation for providing additional information when
 * hovering over a target element.
 */
public class ToolTip extends Tip {

    protected El anchorEl;
    protected String anchorStyle;
    protected Timer dismissTimer;
    protected Timer hideTimer;
    protected Listener<ComponentEvent> listener;
    protected Timer showTimer;
    protected Component target;
    protected Point targetXY = new Point(0, 0);
    protected SafeHtml titleHtml, html;
    protected ToolTipConfig toolTipConfig;

    private Date lastActive;

    /**
     * Creates a new tool tip.
     */
    public ToolTip() {
        toolTipConfig = new ToolTipConfig();
        lastActive = new Date();
        monitorWindowResize = true;
    }

    /**
     * Creates a new tool tip.
     * 
     * @param target the target widget
     */
    public ToolTip(Component target) {
        this();
        initTarget(target);
    }

    /**
     * Creates a new tool tip for the given target.
     * 
     * @param target the target widget
     */
    public ToolTip(Component target, ToolTipConfig config) {
        this();
        updateConfig(config);
        initTarget(target);
    }

    /**
     * Returns the quick show interval.
     * 
     * @return the quick show interval
     */
    public int getQuickShowInterval() {
        return quickShowInterval;
    }

    /**
     * Returns the current tool tip config.
     * 
     * @return the tool tip config
     */
    public ToolTipConfig getToolTipConfig() {
        return toolTipConfig;
    }

    @Override
    public void hide() {
        clearTimers();
        lastActive = new Date();
        super.hide();
    }

    public void initTarget(final Component target) {
        if (this.target != null) {
            this.target.removeListener(Events.OnMouseOver, listener);
            this.target.removeListener(Events.OnMouseOut, listener);
            this.target.removeListener(Events.OnMouseMove, listener);
            this.target.removeListener(Events.Hide, listener);
            this.target.removeListener(Events.Detach, listener);
            this.target.removeListener(Events.Render, listener);
            if (GXT.isFocusManagerEnabled()) {
                this.target.removeListener(Events.OnFocus, listener);
                this.target.removeListener(Events.OnBlur, listener);
                this.target.removeListener(Events.OnKeyDown, listener);
            }
        }

        this.target = target;
        if (listener == null) {
            listener = new Listener<ComponentEvent>() {
                public void handleEvent(ComponentEvent be) {
                    Element source = target.getElement();
                    EventType type = be.getType();
                    if (type == Events.OnMouseOver) {
                        EventTarget from = be.getEvent().getRelatedEventTarget();
                        if (from == null || (Element.is(source) && Element.is(from)
                                && !DOM.isOrHasChild(source, (Element) Element.as(from)))) {
                            onTargetOver(be);
                        }
                    } else if (type == Events.OnMouseOut) {
                        EventTarget to = be.getEvent().getRelatedEventTarget();
                        if (to == null || (Element.is(source) && Element.is(to)
                                && !DOM.isOrHasChild(source, (Element) Element.as(to)))) {
                            onTargetOut(be);
                        }
                    } else if (type == Events.OnMouseMove) {
                        onMouseMove(be);
                    } else if (type == Events.Hide || type == Events.Detach) {
                        hide();
                    } else if (type == Events.OnFocus) {
                        if (GXT.isFocusManagerEnabled()) {
                            targetXY = be.getXY();
                            targetXY.y += target.getOffsetHeight();
                            targetXY.x += target.getOffsetWidth();
                            show();
                        }
                    } else if (type == Events.OnBlur) {
                        if (GXT.isFocusManagerEnabled() && !isClosable()) {
                            hide();
                        }
                    } else if (type == Events.OnKeyDown) {
                        if (GXT.isFocusManagerEnabled() && be.getKeyCode() == KeyCodes.KEY_ESCAPE) {
                            target.getFocusSupport().setIgnore(true);
                            hide();
                        }
                    }
                }
            };
        }
        if (target != null) {
            target.addListener(Events.OnMouseOver, listener);
            target.addListener(Events.Render, listener);
            target.addListener(Events.OnMouseOut, listener);
            target.addListener(Events.OnMouseMove, listener);
            target.addListener(Events.Hide, listener);
            target.addListener(Events.Detach, listener);
            if (GXT.isFocusManagerEnabled()) {
                this.target.addListener(Events.OnFocus, listener);
                this.target.addListener(Events.OnBlur, listener);
                this.target.addListener(Events.OnKeyDown, listener);
            }
            target.sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEMOVE);
        }
    }

    /**
     * Sets the quick show interval (defaults to 250).
     * 
     * @param quickShowInterval the quick show interval
     */
    public void setQuickShowInterval(int quickShowInterval) {
        this.quickShowInterval = quickShowInterval;
    }

    @Override
    public void show() {
        if (disabled)
            return;
        String origAnchor = null;
        boolean origConstrainPosition = false;
        if (toolTipConfig.getAnchor() != null) {
            origAnchor = toolTipConfig.getAnchor();
            // pre-show it off screen so that the el will have dimensions
            // for positioning calcs when getting xy next
            showAt(-1000, -1000);
            origConstrainPosition = this.constrainPosition;
            constrainPosition = false;
        }
        showAt(getTargetXY(0));

        if (toolTipConfig.getAnchor() != null) {
            anchorEl.show();
            syncAnchor();
            constrainPosition = origConstrainPosition;
            toolTipConfig.setAnchor(origAnchor);
        } else {
            anchorEl.hide();
        }
    }

    @Override
    public void showAt(int x, int y) {
        if (disabled)
            return;
        lastActive = new Date();
        clearTimers();
        super.showAt(x, y);
        if (toolTipConfig.getAnchor() != null) {
            anchorEl.show();
            syncAnchor();
        } else {
            anchorEl.hide();
        }
        if (toolTipConfig.getDismissDelay() > 0 && toolTipConfig.isAutoHide() && !toolTipConfig.isCloseable()) {
            dismissTimer = new Timer() {
                public void run() {
                    hide();
                }
            };
            dismissTimer.schedule(toolTipConfig.getDismissDelay());
        }
    }

    /**
     * Updates the tool tip with the given config.
     * 
     * @param config the tool tip config
     */
    public void update(ToolTipConfig config) {
        updateConfig(config);
        if (isRendered() && isAttached()) {
            updateContent();
            doAutoWidth();
        }
    }

    protected void afterRender() {
        super.afterRender();
        anchorEl.setStyleAttribute("zIndex", el().getZIndex() + 1);
    }

    protected void clearTimer(String timer) {
        if (timer.equals("hide")) {
            if (hideTimer != null) {
                hideTimer.cancel();
                hideTimer = null;
            }
        } else if (timer.equals("dismiss")) {
            if (dismissTimer != null) {
                dismissTimer.cancel();
                dismissTimer = null;
            }
        } else if (timer.equals("show")) {
            if (showTimer != null) {
                showTimer.cancel();
                showTimer = null;
            }
        }
    }

    protected void clearTimers() {
        clearTimer("show");
        clearTimer("dismiss");
        clearTimer("hide");
    }

    protected void delayHide() {
        if (isAttached() && hideTimer == null && toolTipConfig.isAutoHide() && !toolTipConfig.isCloseable()) {
            if (toolTipConfig.getHideDelay() == 0) {
                hide();
                return;
            }
            hideTimer = new Timer() {
                public void run() {
                    hide();
                }
            };
            hideTimer.schedule(toolTipConfig.getHideDelay());
        }
    }

    protected void delayShow() {
        if (!isAttached() && showTimer == null) {
            if ((new Date().getTime() - lastActive.getTime()) < quickShowInterval) {
                show();
            } else {
                if (toolTipConfig.getShowDelay() > 0) {
                    showTimer = new Timer() {
                        public void run() {
                            show();
                        }
                    };
                    showTimer.schedule(toolTipConfig.getShowDelay());
                } else {
                    show();
                }
            }
        } else if (isAttached()) {
            show();
        }
    }

    protected String getAnchorAlign() {
        if (toolTipConfig.getAnchor().equals("top")) {
            return "tl-bl";
        } else if (toolTipConfig.getAnchor().equals("left")) {
            return "tl-tr";
        } else if (toolTipConfig.getAnchor().equals("right")) {
            return "tr-tl";
        }

        return "bl-tl";
    }

    protected int[] getOffsets() {
        int[] offsets;
        char ap = toolTipConfig.getAnchor().charAt(0);
        if (toolTipConfig.isAnchorToTarget() && !toolTipConfig.isTrackMouse()) {
            switch (ap) {
            case 't':
                offsets = new int[] { 0, 9 };
                break;
            case 'b':
                offsets = new int[] { 0, -13 };
                break;
            case 'r':
                offsets = new int[] { -13, 0 };
                break;
            default:
                offsets = new int[] { 9, 0 };
                break;
            }
        } else {
            int anchorOffset = toolTipConfig.getAnchorOffset();
            switch (ap) {
            case 't':
                offsets = new int[] { -15 - anchorOffset, 30 };
                break;
            case 'b':
                offsets = new int[] { -19 - anchorOffset, -13 - el().dom.getOffsetHeight() };
                break;
            case 'r':
                offsets = new int[] { -15 - el().dom.getOffsetWidth(), -13 - anchorOffset };
                break;
            default:
                offsets = new int[] { 25, -13 - anchorOffset };
                break;
            }
        }
        int[] mouseOffset = toolTipConfig.getMouseOffset();
        if (mouseOffset != null) {
            offsets[0] += mouseOffset[0];
            offsets[1] += mouseOffset[1];
        }

        return offsets;
    }

    protected void onMouseMove(ComponentEvent ce) {
        targetXY = ce.getXY();
        if (isRendered() && isAttached() && toolTipConfig.isTrackMouse()) {
            String origAnchor = toolTipConfig.getAnchor();
            Point p = getTargetXY(0);
            toolTipConfig.setAnchor(origAnchor);
            if (constrainPosition) {
                p = el().adjustForConstraints(p);
            }
            setPagePosition(p);
        }
    }

    protected void onRender(Element target, int index) {
        super.onRender(target, index);
        anchorEl = new El(DOM.createDiv());
        anchorEl.addStyleName("x-tip-anchor");
        el().appendChild(anchorEl.dom);
        el().addStyleName("x-ignore");
    }

    protected void onTargetOut(ComponentEvent ce) {
        if (disabled) {
            return;
        }
        clearTimer("show");
        delayHide();

    }

    protected void onTargetOver(ComponentEvent ce) {
        if (disabled || !ce.within(target.getElement())) {
            return;
        }

        clearTimer("hide");
        targetXY = ce.getXY();
        delayShow();
    }

    @Override
    protected void onWindowResize(int width, int height) {
        super.onWindowResize(width, height);
        // this can only be reached if the tooltip is already visible, show it again
        // to sync anchor
        show();
    }

    protected void syncAnchor() {
        String anchorPos, targetPos;
        int[] offset;
        int anchorOffset = toolTipConfig.getAnchorOffset();
        switch (toolTipConfig.getAnchor().charAt(0)) {
        case 't':
            anchorPos = "b";
            targetPos = "tl";
            offset = new int[] { 20 + anchorOffset, 2 };
            break;
        case 'r':
            anchorPos = "l";
            targetPos = "tr";
            offset = new int[] { -2, 11 + anchorOffset };
            break;
        case 'b':
            anchorPos = "t";
            targetPos = "bl";
            offset = new int[] { 20 + anchorOffset, -2 };
            break;
        default:
            anchorPos = "r";
            targetPos = "tl";
            offset = new int[] { 2, 11 + anchorOffset };
            break;
        }
        anchorEl.alignTo(el().dom, anchorPos + "-" + targetPos, offset);
    }

    @Override
    protected void updateContent() {
        getHeader().setHtml(titleHtml);
        // show header or not
        getHeader().el().selectNode("#" + getHeader().getId() + "-label")
                .setVisible(SafeGxt.isNullOrEmpty(titleHtml));

        if (toolTipConfig.getTemplate() != null) {
            Params p = toolTipConfig.getParams();
            if (p == null)
                p = new Params();
            p.set("text", html);
            p.set("title", titleHtml);
            toolTipConfig.getTemplate().overwrite(getBody().dom, p);
        } else {
            getBody().update(SafeGxt.emptyToNbSpace(html));
        }
    }

    private Point getTargetXY(int targetCounter) {
        if (toolTipConfig.getAnchor() != null) {
            targetCounter++;
            int[] offsets = getOffsets();
            Point xy = (toolTipConfig.isAnchorToTarget() && !toolTipConfig.isTrackMouse())
                    ? el().getAlignToXY(target.el().dom, getAnchorAlign(), null)
                    : targetXY;

            int dw = XDOM.getViewWidth(false) - 5;
            int dh = XDOM.getViewHeight(false) - 5;
            int scrollX = XDOM.getBodyScrollLeft() + 5;
            int scrollY = XDOM.getBodyScrollTop() + 5;

            int[] axy = new int[] { xy.x + offsets[0], xy.y + offsets[1] };
            Size sz = getSize();
            Region r = target.el().getRegion();
            anchorEl.removeStyleName(anchorStyle);

            // if we are not inside valid ranges we try to switch the anchor
            if (!((toolTipConfig.getAnchor().equals("top") && (sz.height + offsets[1] + scrollY < dh - r.bottom))
                    || (toolTipConfig.getAnchor().equals("right") && (sz.width + offsets[0] + scrollX < r.left))
                    || (toolTipConfig.getAnchor().equals("bottom") && (sz.height + offsets[1] + scrollY < r.top))
                    || (toolTipConfig.getAnchor().equals("left")
                            && (sz.width + offsets[0] + scrollX < dw - r.right)))
                    && targetCounter < 4) {
                if (sz.width + offsets[0] + scrollX < dw - r.right) {
                    toolTipConfig.setAnchor("left");
                    return getTargetXY(targetCounter);
                }
                if (sz.width + offsets[0] + scrollX < r.left) {
                    toolTipConfig.setAnchor("right");
                    return getTargetXY(targetCounter);
                }
                if (sz.height + offsets[1] + scrollY < dh - r.bottom) {
                    toolTipConfig.setAnchor("top");
                    return getTargetXY(targetCounter);
                }
                if (sz.height + offsets[1] + scrollY < r.top) {
                    toolTipConfig.setAnchor("bottom");
                    return getTargetXY(targetCounter);
                }
            }

            anchorStyle = "x-tip-anchor-" + toolTipConfig.getAnchor();
            anchorEl.addStyleName(anchorStyle);
            targetCounter = 0;
            return new Point(axy[0], axy[1]);

        } else {
            int x = targetXY.x;
            int y = targetXY.y;

            int[] mouseOffset = toolTipConfig.getMouseOffset();
            if (mouseOffset != null) {
                x += mouseOffset[0];
                y += mouseOffset[1];
            }
            return new Point(x, y);
        }

    }

    private void updateConfig(ToolTipConfig config) {
        this.toolTipConfig = config;
        if (!config.isEnabled()) {
            clearTimers();
            hide();
        }
        setMinWidth(config.getMinWidth());
        setMaxWidth(config.getMaxWidth());
        setClosable(config.isCloseable());
        html = config.getHtml();
        titleHtml = config.getTitleHtml();
    }

}