/* * Copyright (C) 2014 University of Dundee & Open Microscopy Environment. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.openmicroscopy.shoola.util.ui; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPopupMenu; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.plaf.FontUIResource; /** * Provides a clickable tooltip; Consists of a tooltip text and an action, * which is displayed as 'link' the user can click on. * The behaviour is slightly different from the Swing toolip as it * stays open as long the mouse stays above the component the tooltip * is attached to or the tooltip itself. * * @author Dominik Lindner      * <a href="mailto:d.lindner@dundee.ac.uk">d.lindner@dundee.ac.uk</a> */ public class ClickableTooltip { /** Delay until the tooltip is shown */ private static final int DEFAULT_STARTUP_DELAY = 500; /** Delay until the tooltip vanishes again */ private static final int DEFAULT_SHOW_TIME = 500; /** Default x axis offset to the cursor where the tooltip is shown */ private static final int DEFAULT_X_OFFSET = 10; /** Default y axis offset to the cursor where the tooltip is shown */ private static final int DEFAULT_Y_OFFSET = 10; /** The color for the 'link' text */ private static final Color LINK_COLOR = Color.BLUE; /** The width of the empty border around the tooltip */ private static final int INSETS = 3; /** The tooltip which is actually a JPopupmenu */ private JPopupMenu popupMenu; /** The component the tooltip is attached to */ private JComponent component; /** Reference to the current cursor position where the tooltip's getting to be shown */ private Point location; /** The timers providing the show/hide delays */ private Timer showTimer, hideTimer; /** * Creates a ClickableTooltip with default settings * * @param text The tooltip text * @param action The action which should be provided to the user */ public ClickableTooltip(String text, Action action) { this(text, action, DEFAULT_STARTUP_DELAY, DEFAULT_SHOW_TIME); } /** * Creates a ClickableTooltip with custom show/hide delays * * @param text The tooltip text * @param action The action which should be provided to the user * @param startupDelay Delay in ms before the tooltip should be shown * @param showTime Delay in ms after which the tooltip vanishes when the cursor moved away from the component or the tooltip area */ public ClickableTooltip(final String text, final Action action, final int startupDelay, final int showTime) { Color c = UIUtilities.TOOLTIP_COLOR; Font f = (FontUIResource) UIManager.get("ToolTip.font"); popupMenu = new JPopupMenu(); popupMenu.setLayout(new BoxLayout(popupMenu, BoxLayout.PAGE_AXIS)); popupMenu.setBorder(BorderFactory.createEmptyBorder(INSETS, INSETS, INSETS, INSETS)); popupMenu.setBackground(c); popupMenu.setFont(f); if(text!=null) { JLabel desc = new JLabel(text); desc.setBackground(c); desc.setFont(f); popupMenu.add(desc); } final JLabel label = new JLabel(); label.setForeground(LINK_COLOR); label.setBackground(c); label.setFont(f); label.setText((String)action.getValue(Action.NAME)); label.addMouseListener(new MouseAdapter() { Cursor defaultCursor = null; public void mouseReleased(MouseEvent e) { action.actionPerformed(new ActionEvent(this, -1, (String) action.getValue(Action.ACTION_COMMAND_KEY))); // if the user clicks on the 'link' remove tooltip popupMenu.setVisible(false); } @Override public void mouseEntered(MouseEvent e) { defaultCursor = label.getCursor(); label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); hideTimer.stop(); } @Override public void mouseExited(MouseEvent e) { if(defaultCursor!=null) { label.setCursor(defaultCursor); } hideTimer.restart(); } }); popupMenu.add(label); showTimer = new Timer(startupDelay, new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { popupMenu.show(component, location.x, location.y); showTimer.stop(); } }); hideTimer = new Timer(startupDelay+showTime, new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { popupMenu.setVisible(false); hideTimer.stop(); } }); } /** * Attaches this tooltip to a certain component * * @param component See above. */ public void attach(final JComponent component) { attach(component, DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET); } /** * Attaches this tooltip to a certain component * * @param component See above. * @param xOffset The x axis offset where the tooltip is to be shown * @param yOffset The y axis offset where the tooltiop is to be shown */ public void attach(final JComponent component, final int xOffset, final int yOffset) { this.component = component; component.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { location = e.getPoint(); location.translate(xOffset, yOffset); showTimer.restart(); hideTimer.stop(); } public void mouseExited(MouseEvent e) { showTimer.stop(); hideTimer.restart(); } }); popupMenu.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { hideTimer.stop(); } public void mouseExited(MouseEvent e) { Rectangle componentBounds = new Rectangle(component.getLocationOnScreen(), new Dimension(component.getWidth(), component.getHeight())); if(!componentBounds.contains(e.getLocationOnScreen())) { hideTimer.restart(); } } }); } }