1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.plaf.basic;
  27 
  28 import sun.swing.SwingUtilities2;
  29 import java.awt.*;
  30 import java.beans.PropertyChangeEvent;
  31 import java.beans.PropertyChangeListener;
  32 
  33 import javax.swing.*;
  34 import javax.swing.BorderFactory;
  35 import javax.swing.border.Border;
  36 import javax.swing.plaf.ToolTipUI;
  37 import javax.swing.plaf.ComponentUI;
  38 import javax.swing.plaf.UIResource;
  39 import javax.swing.text.View;
  40 
  41 
  42 /**
  43  * Standard tool tip L&F.
  44  *
  45  * @author Dave Moore
  46  */
  47 public class BasicToolTipUI extends ToolTipUI
  48 {
  49     static BasicToolTipUI sharedInstance = new BasicToolTipUI();
  50     /**
  51      * Global <code>PropertyChangeListener</code> that
  52      * <code>createPropertyChangeListener</code> returns.
  53      */
  54     private static PropertyChangeListener sharedPropertyChangedListener;
  55 
  56     private PropertyChangeListener propertyChangeListener;
  57 
  58     /**
  59      * Returns the instance of {@code BasicToolTipUI}.
  60      *
  61      * @param c a component
  62      * @return the instance of {@code BasicToolTipUI}
  63      */
  64     public static ComponentUI createUI(JComponent c) {
  65         return sharedInstance;
  66     }
  67 
  68     /**
  69      * Constructs a new instance of {@code BasicToolTipUI}.
  70      */
  71     public BasicToolTipUI() {
  72         super();
  73     }
  74 
  75     public void installUI(JComponent c) {
  76         super.installUI(c);
  77         installDefaults(c);
  78         installComponents(c);
  79         installListeners(c);
  80     }
  81 
  82     public void uninstallUI(JComponent c) {
  83         // REMIND: this is NOT getting called
  84         uninstallDefaults(c);
  85         uninstallComponents(c);
  86         uninstallListeners(c);
  87     }
  88 
  89     /**
  90      * Installs default properties.
  91      *
  92      * @param c a component
  93      */
  94     protected void installDefaults(JComponent c){
  95         LookAndFeel.installColorsAndFont(c, "ToolTip.background",
  96                 "ToolTip.foreground",
  97                 "ToolTip.font");
  98         LookAndFeel.installProperty(c, "opaque", Boolean.TRUE);
  99         componentChanged(c);
 100     }
 101 
 102     /**
 103      * Uninstalls default properties.
 104      *
 105      * @param c a component
 106      */
 107     protected void uninstallDefaults(JComponent c){
 108         LookAndFeel.uninstallBorder(c);
 109     }
 110 
 111     /* Unfortunately this has to remain private until we can make API additions.
 112      */
 113     private void installComponents(JComponent c){
 114         BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText());
 115     }
 116 
 117     /* Unfortunately this has to remain private until we can make API additions.
 118      */
 119     private void uninstallComponents(JComponent c){
 120         BasicHTML.updateRenderer(c, "");
 121     }
 122 
 123     /**
 124      * Registers listeners.
 125      *
 126      * @param c a component
 127      */
 128     protected void installListeners(JComponent c) {
 129         propertyChangeListener = createPropertyChangeListener(c);
 130 
 131         c.addPropertyChangeListener(propertyChangeListener);
 132     }
 133 
 134     /**
 135      * Unregisters listeners.
 136      *
 137      * @param c a component
 138      */
 139     protected void uninstallListeners(JComponent c) {
 140         c.removePropertyChangeListener(propertyChangeListener);
 141 
 142         propertyChangeListener = null;
 143     }
 144 
 145     /* Unfortunately this has to remain private until we can make API additions.
 146      */
 147     private PropertyChangeListener createPropertyChangeListener(JComponent c) {
 148         if (sharedPropertyChangedListener == null) {
 149             sharedPropertyChangedListener = new PropertyChangeHandler();
 150         }
 151         return sharedPropertyChangedListener;
 152     }
 153 
 154     public void paint(Graphics g, JComponent c) {
 155         Font font = c.getFont();
 156         FontMetrics metrics = SwingUtilities2.getFontMetrics(c, g, font);
 157         Dimension size = c.getSize();
 158 
 159         g.setColor(c.getForeground());
 160         // fix for bug 4153892
 161         String tipText = ((JToolTip)c).getTipText();
 162         if (tipText == null) {
 163             tipText = "";
 164         }
 165 
 166         Insets insets = c.getInsets();
 167         Rectangle paintTextR = new Rectangle(
 168             insets.left + 3,
 169             insets.top,
 170             size.width - (insets.left + insets.right) - 6,
 171             size.height - (insets.top + insets.bottom));
 172         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 173         if (v != null) {
 174             v.paint(g, paintTextR);
 175         } else {
 176             g.setFont(font);
 177             getTextUIDrawing().drawString(c, g, tipText, paintTextR.x,
 178                                   paintTextR.y + metrics.getAscent());
 179         }
 180     }
 181 
 182     public Dimension getPreferredSize(JComponent c) {
 183         Font font = c.getFont();
 184         FontMetrics fm = c.getFontMetrics(font);
 185         Insets insets = c.getInsets();
 186 
 187         Dimension prefSize = new Dimension(insets.left+insets.right,
 188                                            insets.top+insets.bottom);
 189         String text = ((JToolTip)c).getTipText();
 190 
 191         if ((text == null) || text.equals("")) {
 192             text = "";
 193         }
 194         else {
 195             View v = (c != null) ? (View) c.getClientProperty("html") : null;
 196             if (v != null) {
 197                 prefSize.width += (int) v.getPreferredSpan(View.X_AXIS) + 6;
 198                 prefSize.height += (int) v.getPreferredSpan(View.Y_AXIS);
 199             } else {
 200                 prefSize.width += getTextUIDrawing().getStringWidth(c,fm,text) + 6;
 201                 prefSize.height += fm.getHeight();
 202             }
 203         }
 204         return prefSize;
 205     }
 206 
 207     public Dimension getMinimumSize(JComponent c) {
 208         Dimension d = getPreferredSize(c);
 209         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 210         if (v != null) {
 211             d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
 212         }
 213         return d;
 214     }
 215 
 216     public Dimension getMaximumSize(JComponent c) {
 217         Dimension d = getPreferredSize(c);
 218         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 219         if (v != null) {
 220             d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
 221         }
 222         return d;
 223     }
 224 
 225     /**
 226      * Invoked when the <code>JCompoment</code> associated with the
 227      * <code>JToolTip</code> has changed, or at initialization time. This
 228      * should update any state dependant upon the <code>JComponent</code>.
 229      *
 230      * @param c the JToolTip the JComponent has changed on.
 231      */
 232     private void componentChanged(JComponent c) {
 233         JComponent comp = ((JToolTip)c).getComponent();
 234 
 235         if (comp != null && !(comp.isEnabled())) {
 236             // For better backward compatibility, only install inactive
 237             // properties if they are defined.
 238             if (UIManager.getBorder("ToolTip.borderInactive") != null) {
 239                 LookAndFeel.installBorder(c, "ToolTip.borderInactive");
 240             }
 241             else {
 242                 LookAndFeel.installBorder(c, "ToolTip.border");
 243             }
 244             if (UIManager.getColor("ToolTip.backgroundInactive") != null) {
 245                 LookAndFeel.installColors(c,"ToolTip.backgroundInactive",
 246                                           "ToolTip.foregroundInactive");
 247             }
 248             else {
 249                 LookAndFeel.installColors(c,"ToolTip.background",
 250                                           "ToolTip.foreground");
 251             }
 252         } else {
 253             LookAndFeel.installBorder(c, "ToolTip.border");
 254             LookAndFeel.installColors(c, "ToolTip.background",
 255                                       "ToolTip.foreground");
 256         }
 257     }
 258 
 259 
 260     private static class PropertyChangeHandler implements
 261                                  PropertyChangeListener {
 262         public void propertyChange(PropertyChangeEvent e) {
 263             String name = e.getPropertyName();
 264             if (name.equals("tiptext") || "font".equals(name) ||
 265                 "foreground".equals(name)) {
 266                 // remove the old html view client property if one
 267                 // existed, and install a new one if the text installed
 268                 // into the JLabel is html source.
 269                 JToolTip tip = ((JToolTip) e.getSource());
 270                 String text = tip.getTipText();
 271                 BasicHTML.updateRenderer(tip, text);
 272             }
 273             else if ("component".equals(name)) {
 274                 JToolTip tip = ((JToolTip) e.getSource());
 275 
 276                 if (tip.getUI() instanceof BasicToolTipUI) {
 277                     ((BasicToolTipUI)tip.getUI()).componentChanged(tip);
 278                 }
 279             }
 280         }
 281     }
 282 }