/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.ui.components.richtooltip; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.swing.JComponent; import javax.swing.Popup; import javax.swing.PopupFactory; /* * Copyright (c) 2005-2009 Flamingo Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Flamingo Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Rich tooltip for command buttons. * * <p> * In its most basic form, the rich tooltip has a title and one (possible * multiline) description text: * </p> * * <pre> * +--------------------------------+ * | Title | * | Some description text | * +--------------------------------+ * </pre> * * <p> * The {@link #addDescriptionSection(String)} can be used to add multiple * sections to the description: * </p> * * <pre> * +--------------------------------+ * | Title | * | First multiline | * | description section | * | | * | Second multiline | * | description section | * | | * | Third multiline | * | description section | * +--------------------------------+ * </pre> * * <p> * The {@link #setMainImage(Image)} can be used to place an image below the * title and to the left of the description sections: * </p> * * <pre> * +--------------------------------+ * | Title | * | ******* First multiline | * | *image* description section | * | ******* | * | Second multiline | * | description section | * +--------------------------------+ * </pre> * * <p> * The {@link #addFooterSection(String)} can be used to add (possibly) multiple * footer sections that will be shown below a horizontal separator: * </p> * * <pre> * +--------------------------------+ * | Title | * | First multiline | * | description section | * | | * | Second multiline | * | description section | * |--------------------------------| * | A multiline footer section | * | placed below a separator | * +--------------------------------+ * </pre> * * <p> * The {@link #setFooterImage(Image)} can be used to place an image to the left * of the footer sections: * </p> * * <pre> * +--------------------------------+ * | Title | * | First multiline | * | description section | * | | * | Second multiline | * | description section | * |--------------------------------| * | ******* A multiline | * | *image* footer section | * | ******* | * +--------------------------------+ * </pre> * * <p> * Here is a fully fledged rich tooltip that shows all these APIs in action: * </p> * * <pre> * +--------------------------------+ * | Title | * | ******* First multiline | * | *image* description section | * | ******* | * | Second multiline | * | description section | * |--------------------------------| * | ******* First multiline | * | *image* footer section | * | ******* | * | Second multiline | * | footer section | * +--------------------------------+ * </pre> * * @author Kirill Grouchnikov */ public class RichTooltip { /** * The main title of this tooltip. * * @see #RichTooltip(String, String) * @see #setTitle(String) * @see #getTitle() */ protected String title; /** * The main image of this tooltip. Can be <code>null</code>. * * @see #getMainImage() * @see #setMainImage(Image) */ protected Image mainImage; /** * The description sections of this tooltip. * * @see #RichTooltip(String, String) * @see #addDescriptionSection(String) * @see #getDescriptionSections() */ protected List<String> descriptionSections; /** * The footer image of this tooltip. Can be <code>null</code>. * * @see #getFooterImage() * @see #setFooterImage(Image) */ protected Image footerImage; /** * The footer sections of this tooltip. Can be empty. * * @see #addFooterSection(String) * @see #getFooterSections() */ protected List<String> footerSections; /** * Creates an empty tooltip. */ public RichTooltip() { } /** * Creates a tooltip with the specified title and description section. * * @param title * Tooltip title. * @param descriptionSection * Tooltip main description section. */ public RichTooltip(String title, String descriptionSection) { this.setTitle(title); this.addDescriptionSection(descriptionSection); } private Popup tipWindow; private boolean tipShowing = false; public void showTooltip(JComponent component) { if (component == null || !component.isShowing()) { return; } Dimension size; Point screenLocation = component.getLocationOnScreen(); Point location = new Point(); GraphicsConfiguration gc; gc = component.getGraphicsConfiguration(); Rectangle sBounds = gc.getBounds(); Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc); // Take into account screen insets, decrease viewport sBounds.x += screenInsets.left; sBounds.y += screenInsets.top; sBounds.width -= (screenInsets.left + screenInsets.right); sBounds.height -= (screenInsets.top + screenInsets.bottom); hideTooltip(); JRichTooltipPanel tip = new JRichTooltipPanel(this); size = tip.getPreferredSize(); // display directly below or above it location.x = screenLocation.x; location.y = screenLocation.y + component.getHeight(); if ((location.y + size.height) > (sBounds.y + sBounds.height)) { location.y = screenLocation.y - size.height; } // Tweak the X location to not overflow the screen if (location.x < sBounds.x) { location.x = sBounds.x; } else if (location.x - sBounds.x + size.width > sBounds.width) { location.x = sBounds.x + Math.max(0, sBounds.width - size.width); } PopupFactory popupFactory = PopupFactory.getSharedInstance(); tipWindow = popupFactory.getPopup(component, tip, location.x, location.y); tipWindow.show(); tipShowing = true; } public void hideTooltip() { if (tipWindow != null) { tipWindow.hide(); tipWindow = null; tipShowing = false; } } /** * Sets the title for this tooltip. * * @param title * The new tooltip title. */ public void setTitle(String title) { this.title = title; } /** * Sets the main image for this tooltip. * * @param image * The main image for this tooltip. * @see #getMainImage() * @see #addDescriptionSection(String) */ public void setMainImage(Image image) { this.mainImage = image; } /** * Adds the specified description section to this tooltip. * * @param section * The description section to add. * @see #getDescriptionSections() * @see #setMainImage(Image) * @see #setTitle(String) */ public void addDescriptionSection(String section) { if (this.descriptionSections == null) { this.descriptionSections = new LinkedList<String>(); } this.descriptionSections.add(section); } /** * Sets the footer image for this tooltip. * * @param image * The footer image for this tooltip. * @see #getFooterImage() * @see #addFooterSection(String) */ public void setFooterImage(Image image) { this.footerImage = image; } /** * Adds the specified footer section to this tooltip. * * @param section * The footer section to add. * @see #getFooterSections() * @see #setFooterImage(Image) */ public void addFooterSection(String section) { if (this.footerSections == null) { this.footerSections = new LinkedList<String>(); } this.footerSections.add(section); } /** * Returns the main title of this tooltip. * * @return The main title of this tooltip. * @see #RichTooltip(String, String) * @see #setTitle(String) */ public String getTitle() { return this.title; } /** * Returns the main image of this tooltip. Can return <code>null</code>. * * @return The main image of this tooltip. * @see #setMainImage(Image) * @see #getDescriptionSections() */ public Image getMainImage() { return this.mainImage; } /** * Returns an unmodifiable list of description sections of this tooltip. * Guaranteed to return a non-<code>null</code> list. * * @return An unmodifiable list of description sections of this tooltip. * @see #RichTooltip(String, String) * @see #addDescriptionSection(String) * @see #getTitle() * @see #getMainImage() */ public List<String> getDescriptionSections() { if (this.descriptionSections == null) { return Collections.EMPTY_LIST; } return Collections.unmodifiableList(this.descriptionSections); } /** * Returns the footer image of this tooltip. Can return <code>null</code>. * * @return The footer image of this tooltip. * @see #setFooterImage(Image) * @see #getFooterSections() */ public Image getFooterImage() { return this.footerImage; } /** * Returns an unmodifiable list of footer sections of this tooltip. * Guaranteed to return a non-<code>null</code> list. * * @return An unmodifiable list of footer sections of this tooltip. * @see #addFooterSection(String) * @see #getFooterImage() */ public List<String> getFooterSections() { if (this.footerSections == null) { return Collections.EMPTY_LIST; } return Collections.unmodifiableList(this.footerSections); } }