/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Distributable under LGPL license. * See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.desktoputil; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.*; import javax.swing.plaf.metal.*; import net.java.sip.communicator.util.*; /** * The tooltip shown over a contact in the contact list. * * @author Yana Stamcheva */ public class ExtendedTooltip extends JToolTip implements AncestorListener, WindowFocusListener { private static final Logger logger = Logger.getLogger(ExtendedTooltip.class); /** * Class id key used in UIDefaults. */ private static final String uiClassID = ExtendedTooltip.class.getName() + "ToolTipUI"; /** * Adds the ui class to UIDefaults. */ static { UIManager.getDefaults().put(uiClassID, ImageToolTipUI.class.getName()); } private final JLabel imageLabel = new JLabel(); private final JLabel titleLabel = new JLabel(); private final JPanel linesPanel = new JPanel(); private final JTextArea bottomTextArea = new JTextArea(); private int textWidth = 0; private int textHeight = 0; private boolean isListViewEnabled; /** * The parent window where this tooltip was created, not the one the * component was added to, but where the focus is when the component is * created/added. */ private Window parentWindow = null; /** * Created a MetaContactTooltip. * @param isListViewEnabled indicates if the list view is enabled */ public ExtendedTooltip(boolean isListViewEnabled) { this.isListViewEnabled = isListViewEnabled; this.setLayout(new BorderLayout()); JPanel mainPanel = new JPanel(new BorderLayout(5, 5)); JPanel centerPanel = new JPanel(new BorderLayout()); mainPanel.setOpaque(false); centerPanel.setOpaque(false); linesPanel.setOpaque(false); bottomTextArea.setOpaque(false); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); if (isListViewEnabled) { linesPanel.setLayout( new BoxLayout(linesPanel, BoxLayout.Y_AXIS)); mainPanel.add(imageLabel, BorderLayout.WEST); mainPanel.add(centerPanel, BorderLayout.CENTER); centerPanel.add(titleLabel, BorderLayout.NORTH); centerPanel.add(linesPanel, BorderLayout.CENTER); } else { titleLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); mainPanel.add(imageLabel, BorderLayout.CENTER); mainPanel.add(titleLabel, BorderLayout.NORTH); } bottomTextArea.setEditable(false); bottomTextArea.setLineWrap(true); bottomTextArea.setWrapStyleWord(true); bottomTextArea.setFont(bottomTextArea.getFont().deriveFont(10f)); mainPanel.add(bottomTextArea, BorderLayout.SOUTH); this.addAncestorListener(this); this.add(mainPanel); } /** * Sets the given image to this tooltip. * * @param imageIcon The image icon to set. */ public void setImage(ImageIcon imageIcon) { imageLabel.setIcon(imageIcon); } /** * Sets the title of the tooltip. The text would be shown in bold on the top * of the tooltip panel. * * @param titleText The title of the tooltip. */ public void setTitle(String titleText) { titleLabel.setText(titleText); Dimension labelSize = ComponentUtils.getStringSize(titleLabel,titleText); recalculateTooltipSize(labelSize.width, labelSize.height); } /** * Adds an icon-string list, which would appear on the right of the image * panel. * * @param icon the icon to show * @param text the name to show */ public void addLine(Icon icon, String text) { JLabel lineLabel = new JLabel( text, icon, JLabel.LEFT); linesPanel.add(lineLabel); Dimension labelSize = calculateLabelSize(lineLabel); recalculateTooltipSize(labelSize.width, labelSize.height); } /** * Adds an icon-string list, which would appear on the right of the image * panel. * * @param icon the icon to show * @param text the name to show * @param leftIndent left indent of the label */ public void addSubLine(Icon icon, String text, int leftIndent) { JLabel lineLabel = new JLabel( text, icon, JLabel.LEFT); lineLabel.setBorder( BorderFactory.createEmptyBorder(0, leftIndent, 0, 0)); lineLabel.setFont(lineLabel.getFont().deriveFont(9f)); lineLabel.setForeground(Color.DARK_GRAY); linesPanel.add(lineLabel); Dimension labelSize = calculateLabelSize(lineLabel); recalculateTooltipSize(labelSize.width + leftIndent, labelSize.height); } /** * Adds the given array of labels as one line in this tool tip. * * @param labels the labels to add */ public void addLine(JLabel[] labels) { Dimension lineSize = null; JPanel labelPanel = null; if (labels.length > 0) { labelPanel = new TransparentPanel( new FlowLayout(FlowLayout.LEFT, 2, 0)); linesPanel.add(labelPanel); } else return; if (labelPanel != null) for (JLabel label : labels) { labelPanel.add(label); if (lineSize == null) lineSize = calculateLabelSize(label); else lineSize = new Dimension( lineSize.width + calculateLabelSize(label).width, lineSize.height); } recalculateTooltipSize(lineSize.width, lineSize.height); } /** * Clear all lines. */ public void removeAllLines() { linesPanel.removeAll(); } /** * Sets the text that would appear on the bottom of the tooltip. * @param text the text to set */ public void setBottomText(String text) { this.bottomTextArea.setText(text); } /** * Calculates label size. * * @param label the label, which size we should calculate * @return the Dimension indicating the label size */ private Dimension calculateLabelSize(JLabel label) { Icon icon = label.getIcon(); String text = label.getText(); int iconWidth = 0; int iconHeight = 0; if (icon != null) { iconWidth = icon.getIconWidth(); iconHeight = icon.getIconHeight(); } int labelWidth = ComponentUtils.getStringWidth(label, text) + iconWidth + label.getIconTextGap(); int textHeight = ComponentUtils.getStringSize(label, text).height; int labelHeight = (iconHeight > textHeight) ? iconHeight : textHeight; return new Dimension(labelWidth, labelHeight); } /** * Re-calculates the tooltip size. * * @param newTextWidth the width of the newly added text that should be * added to the global width * @param newTextHeight the height of the newly added text that should be * added to the global height */ private void recalculateTooltipSize(int newTextWidth, int newTextHeight) { if (textWidth < newTextWidth) textWidth = newTextWidth; textHeight += newTextHeight; } /** * When main windows focus is lost hide the tooltip. * @param e window event. */ @Override public void windowLostFocus(WindowEvent e) { Window popupWindow = SwingUtilities.getWindowAncestor( ExtendedTooltip.this); if ((popupWindow != null) && popupWindow.isVisible() // The popup window should normally be a JWindow, so we // check here explicitly if for some reason we didn't // get something else. && (popupWindow instanceof JWindow)) { if (logger.isInfoEnabled()) logger.info("Tooltip window ancestor to hide: " + popupWindow); popupWindow.setVisible(false); } } /** * Not used. * @param e */ @Override public void windowGainedFocus(WindowEvent e) {} /** * Not used. * @param event */ @Override public void ancestorAdded(AncestorEvent event) { this.parentWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager() .getActiveWindow(); // Hide the tooltip when the parent window hides. if (parentWindow != null) { parentWindow.addWindowFocusListener(this); } } /** * When the tooltip window is disposed elements are removed from it * and this is the time to clear resources. * @param event */ @Override public void ancestorRemoved(AncestorEvent event) { if(this.parentWindow != null) { this.parentWindow.removeWindowFocusListener(this); this.parentWindow = null; } this.removeAncestorListener(this); } /** * Not used. * @param event */ @Override public void ancestorMoved(AncestorEvent event) {} /** * Customized UI for this MetaContactTooltip. */ public static class ImageToolTipUI extends MetalToolTipUI { static ImageToolTipUI sharedInstance = new ImageToolTipUI(); /** * Creates the UI. * @param c * @return */ public static ComponentUI createUI(JComponent c) { return sharedInstance; } /** * Overwrite the UI paint method to do nothing in order fix double * painting of the tooltip text. * @param g the Graphics object * @param c the component used to render the tooltip */ @Override public void paint(Graphics g, JComponent c) {} /** * Override ComponentUI update method to set visibility of bottomText. * @param g Graphics object * @param c the component used to render the tooltip */ @Override public void update(Graphics g, JComponent c) { JTextArea bottomTextArea = ((ExtendedTooltip)c).bottomTextArea; String bottomText = bottomTextArea.getText(); if(bottomText == null || bottomText.length() <= 0) bottomTextArea.setVisible(false); else bottomTextArea.setVisible(true); super.update(g, c); } /** * Returns the size of the given component. * @param c the component used to render the tooltip * @return the size of the given component. */ @Override public Dimension getPreferredSize(JComponent c) { ExtendedTooltip tooltip = (ExtendedTooltip)c; Icon icon = tooltip.imageLabel.getIcon(); int width = 0; if (icon != null) width += icon.getIconWidth(); if (tooltip.isListViewEnabled) width += tooltip.textWidth + 15; else width = tooltip.textWidth > width ? tooltip.textWidth : width; int imageHeight = 0; if (icon != null) imageHeight = icon.getIconHeight(); int height = 0; if (tooltip.isListViewEnabled) { height = imageHeight > tooltip.textHeight ? imageHeight : tooltip.textHeight; } else height = imageHeight + tooltip.textHeight; String bottomText = tooltip.bottomTextArea.getText(); if(bottomText != null && bottomText.length() > 0) { // Seems a little messy, but sets the proper size. tooltip.bottomTextArea.setColumns(5); tooltip.bottomTextArea.setSize(0,0); tooltip.bottomTextArea.setSize( tooltip.bottomTextArea.getPreferredSize()); height += tooltip.bottomTextArea.getPreferredSize().height; } return new Dimension(width, height); } } /** * Returns the name of the L&F class that renders this component. * * @return the string "TreeUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ @Override public String getUIClassID() { return uiClassID; } }