All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.widgets.extra.tooltip.FocusableTip Maven / Gradle / Ivy

Go to download

Widgets graphiques utiles pour tous les développements (pour le moment sans lien avec JAXX).

There is a newer version: 3.0-alpha-17
Show newest version
/*
 * %%Ignore-License
 *
 * 07/29/2009
 *
 * FocusableTip.java - A focusable tool tip, like those in Eclipse.
 * Copyright (C) 2009 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 */
package org.nuiton.jaxx.widgets.extra.tooltip;

import org.apache.commons.lang3.StringUtils;

import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.MouseInputAdapter;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.net.URL;


/**
 * A focusable tool tip, similar to those found in Eclipse.  The user
 * can click in the tip and it becomes a "real," resizable window.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class FocusableTip {

    public static final String DUMMY_TOOL_TIP = "DUMMY";

    protected JComponent attachedComponent;
    protected TipWindow tipWindow;
    protected URL imageBase;
    protected final AttachedComponentListener attachedComponentListener;
    protected final HyperlinkListener hyperlinkListener;
    protected String lastText;
    protected Component lastComponent;
    protected final boolean showCopyContextMenu;

    /** Width with default value. */
    protected int width = 320;

    /** Height with default value. */
    protected int height = 250;

    /**
     * The screen bounds in which the mouse has to stay for the currently
     * displayed tip to stay visible.
     */
    protected final Rectangle tipVisibleBounds;

    /**
     * Margin from mouse cursor at which to draw focusable tip.
     */
    protected static final int MARGIN = 10;

    /**
     * FocusableTip constructor
     *
     * @param attachedComponent component to attach
     */
    public FocusableTip(JComponent attachedComponent) {
        this(attachedComponent, null);
    }

    /**
     * FocusableTip constructor
     *
     * @param attachedComponent component to attach
     * @param listener          hyper link listener to add
     */
    public FocusableTip(JComponent attachedComponent, HyperlinkListener listener) {
        this(attachedComponent, listener, false);
    }

    /**
     * FocusableTip constructor
     *
     * @param attachedComponent   component to attach
     * @param showCopyContextMenu to show copy context menu
     */
    public FocusableTip(JComponent attachedComponent, boolean showCopyContextMenu) {
        this(attachedComponent, null, showCopyContextMenu);
    }

    /**
     * FocusableTip constructor
     *
     * @param attachedComponent   component to attach
     * @param listener            hyper link listener to add
     * @param showCopyContextMenu to show copy context menu
     */
    public FocusableTip(JComponent attachedComponent, HyperlinkListener listener, boolean showCopyContextMenu) {
        setAttachedComponent(attachedComponent);
        this.hyperlinkListener = listener;
        attachedComponentListener = new AttachedComponentListener();
        tipVisibleBounds = new Rectangle();
        this.showCopyContextMenu = showCopyContextMenu;

        attachedComponent.setToolTipText(" ");
    }

    /**
     * Change tip size.
     *
     * @param width  width
     * @param height height
     */
    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;
    }

    /**
     * Get {@link TipWindow} displayed
     *
     * @return TipWindow displayed
     */
    public TipWindow getTipWindows() {
        return tipWindow;
    }

    /**
     * Compute the bounds in which the user can move the mouse without the
     * tip window disappearing.
     */
    protected void computeTipVisibleBounds() {
        // Compute area that the mouse can move in without hiding the
        // tip window. Note that Java 1.4 can only detect mouse events
        // in Java windows, not globally.
        Rectangle r = tipWindow.getBounds();
        Point p = r.getLocation();
        SwingUtilities.convertPointFromScreen(p, attachedComponent);
        r.setLocation(p);
        tipVisibleBounds.setBounds(r.x, r.y - 15, r.width, r.height + 15 * 2);
    }


    protected void createAndShowTipWindow(final MouseEvent e, final Component component, final String text) {

        Window owner = SwingUtilities.getWindowAncestor(attachedComponent);

        if (component == null) {
            tipWindow = new TipWindow(owner, this, text);
        } else {
            tipWindow = new TipWindow(owner, this, component);
        }
        tipWindow.setHyperlinkListener(hyperlinkListener);

        // TODO: Position tip window better (handle RTL, edges of screen, etc.).
        // Wrap in an invokeLater() to work around a JEditorPane issue where it
        // doesn't return its proper preferred size until after it is displayed.
        // See http://forums.sun.com/thread.jspa?forumID=57&threadID=574810
        // for a discussion.
        SwingUtilities.invokeLater(() -> {

            // If a new FocusableTip is requested while another one is
            // *focused* and visible, the focused tip (i.e. "tipWindow")
            // will be disposed of.  If this Runnable is run after the
            // dispose(), tipWindow will be null.  All of this is done on
            // the EDT so no synchronization should be necessary.
            if (tipWindow == null) {
                return;
            }

// Add copy context menu
            if (showCopyContextMenu && component == null) {
                TipUtil.addCopyContextMenu(tipWindow);
            }

            tipWindow.fixSize(width, height);
            ComponentOrientation o = attachedComponent.getComponentOrientation();

            Point p = e.getPoint();
            SwingUtilities.convertPointToScreen(p, attachedComponent);
            int x = o.isLeftToRight() ? (p.x - 10) :
                    (p.x - tipWindow.getWidth() + MARGIN);
            int y = p.y + MARGIN;

            // Ensure tooltip is in the window bounds.
            Dimension ss = tipWindow.getToolkit().getScreenSize();
            x = Math.max(x, 0);
            if (x + tipWindow.getWidth() >= ss.width) {
                x = ss.width - tipWindow.getWidth();
            }
            if (y + tipWindow.getHeight() >= ss.height) { // Go above cursor
                y = p.y - tipWindow.getHeight() - MARGIN;
            }

            tipWindow.setLocation(x, y);
            tipWindow.setVisible(true);
            tipWindow.toFront();
            computeTipVisibleBounds(); // Do after tip is visible
            attachedComponentListener.install(attachedComponent);
            lastText = text;
            lastComponent = component;
        });

    }


    /**
     * Returns the base URL to use when loading images in this focusable tip.
     *
     * @return The base URL to use.
     * @see #setImageBase(URL)
     */
    public URL getImageBase() {
        return imageBase;
    }


	/*
     * Returns localized text for the given key.
	 *
	 * @param key The key into the resource bundle.
	 * @return The localized text.
	 *
	static String getString(String key) {
		return msg.getString(key);
	}*/


    /**
     * Disposes of the focusable tip currently displayed, if any.
     */
    public void possiblyDisposeOfTipWindow() {
        if (tipWindow != null) {
            tipWindow.dispose();
            tipWindow = null;
            attachedComponentListener.uninstall();
            tipVisibleBounds.setBounds(-1, -1, 0, 0);
            lastText = null;
            lastComponent = null;
            attachedComponent.requestFocus();
        }
    }


    void removeListeners() {
        //System.out.println("DEBUG: Removing text area listeners");
        attachedComponentListener.uninstall();
    }


    /**
     * Sets the base URL to use when loading images in this focusable tip.
     *
     * @param url The base URL to use.
     * @see #getImageBase()
     */
    public void setImageBase(URL url) {
        imageBase = url;
    }


    protected void setAttachedComponent(JComponent attachedComponent) {
        this.attachedComponent = attachedComponent;
        // Is okay to do multiple times.
        ToolTipManager.sharedInstance().registerComponent(attachedComponent);
    }

    public void toolTipRequested(MouseEvent e, String text) {

        if (StringUtils.isEmpty(text)) {
            possiblyDisposeOfTipWindow();
            lastText = text;
            return;
        }

        if (lastText == null || text.length() != lastText.length()
                || !text.equals(lastText)) {
            possiblyDisposeOfTipWindow();
            createAndShowTipWindow(e, null, text);
        }

    }

    /*
     * To display specific component in toolTip
     *
     * Note : Be carfull, set toolTipText of attachedComponent.setToolTipText(FocusableTip.DUMMY_TOOL_TIP);
     */
    public void toolTipRequested(MouseEvent e, Component component) {

        if (component == null) {
            possiblyDisposeOfTipWindow();
            lastComponent = component;
            return;
        }

        if (lastComponent == null || !lastComponent.equals(component)) {
            possiblyDisposeOfTipWindow();
            createAndShowTipWindow(e, component, null);
        }

    }


    protected class AttachedComponentListener extends MouseInputAdapter implements
            CaretListener, ComponentListener, FocusListener, KeyListener {

        public void caretUpdate(CaretEvent e) {
            Object source = e.getSource();
            if (source == attachedComponent) {
                possiblyDisposeOfTipWindow();
            }
        }

        public void componentHidden(ComponentEvent e) {
            handleComponentEvent(e);
        }

        public void componentMoved(ComponentEvent e) {
            handleComponentEvent(e);
        }

        public void componentResized(ComponentEvent e) {
            handleComponentEvent(e);
        }

        public void componentShown(ComponentEvent e) {
            handleComponentEvent(e);
        }

        public void focusGained(FocusEvent e) {
        }

        public void focusLost(FocusEvent e) {
            // Only dispose of tip if it wasn't the TipWindow that was clicked
            // "c" can be null, at least on OS X, so we must check that before
            // calling SwingUtilities.getWindowAncestor() to guard against an
            // NPE.
            Component c = e.getOppositeComponent();
            boolean tipClicked = (c instanceof TipWindow) ||
                    (c != null &&
                            SwingUtilities.getWindowAncestor(c) instanceof TipWindow);
            if (!tipClicked) {
                possiblyDisposeOfTipWindow();
            }
        }

        protected void handleComponentEvent(ComponentEvent e) {
            possiblyDisposeOfTipWindow();
        }

        public void install(JComponent attachedComponent) {
            //attachedComponent.addCaretListener(this);
            attachedComponent.addComponentListener(this);
            attachedComponent.addFocusListener(this);
            attachedComponent.addKeyListener(this);
            attachedComponent.addMouseListener(this);
            attachedComponent.addMouseMotionListener(this);
        }

        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                possiblyDisposeOfTipWindow();
            } else if (e.getKeyCode() == KeyEvent.VK_F2) {
                if (tipWindow != null && !tipWindow.getFocusableWindowState()) {
                    tipWindow.actionPerformed(null);
                    e.consume(); // Don't do bookmarking stuff
                }
            }
        }

        public void keyReleased(KeyEvent e) {
        }

        public void keyTyped(KeyEvent e) {
        }

        public void mouseExited(MouseEvent e) {
            // possiblyDisposeOfTipWindow();
        }

        public void mouseMoved(MouseEvent e) {
            if (tipVisibleBounds == null ||
                    !tipVisibleBounds.contains(e.getPoint())) {
                possiblyDisposeOfTipWindow();
            }
        }

        public void uninstall() {
            //attachedComponent.removeCaretListener(this);
            attachedComponent.removeComponentListener(this);
            attachedComponent.removeFocusListener(this);
            attachedComponent.removeKeyListener(this);
            attachedComponent.removeMouseListener(this);
            attachedComponent.removeMouseMotionListener(this);
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy