1 /*
   2  * Copyright (c) 1997, 2018, 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         installDefaults(c);
  77         installComponents(c);
  78         installListeners(c);
  79     }
  80 
  81     public void uninstallUI(JComponent c) {
  82         // REMIND: this is NOT getting called
  83         uninstallDefaults(c);
  84         uninstallComponents(c);
  85         uninstallListeners(c);
  86     }
  87 
  88     /**
  89      * Installs default properties.
  90      *
  91      * @param c a component
  92      */
  93     protected void installDefaults(JComponent c){
  94         LookAndFeel.installColorsAndFont(c, "ToolTip.background",
  95                 "ToolTip.foreground",
  96                 "ToolTip.font");
  97         LookAndFeel.installProperty(c, "opaque", Boolean.TRUE);
  98         componentChanged(c);
  99     }
 100 
 101     /**
 102      * Uninstalls default properties.
 103      *
 104      * @param c a component
 105      */
 106     protected void uninstallDefaults(JComponent c){
 107         LookAndFeel.uninstallBorder(c);
 108     }
 109 
 110     /* Unfortunately this has to remain private until we can make API additions.
 111      */
 112     private void installComponents(JComponent c){
 113         BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText());
 114     }
 115 
 116     /* Unfortunately this has to remain private until we can make API additions.
 117      */
 118     private void uninstallComponents(JComponent c){
 119         BasicHTML.updateRenderer(c, "");
 120     }
 121 
 122     /**
 123      * Registers listeners.
 124      *
 125      * @param c a component
 126      */
 127     protected void installListeners(JComponent c) {
 128         propertyChangeListener = createPropertyChangeListener(c);
 129 
 130         c.addPropertyChangeListener(propertyChangeListener);
 131     }
 132 
 133     /**
 134      * Unregisters listeners.
 135      *
 136      * @param c a component
 137      */
 138     protected void uninstallListeners(JComponent c) {
 139         c.removePropertyChangeListener(propertyChangeListener);
 140 
 141         propertyChangeListener = null;
 142     }
 143 
 144     /* Unfortunately this has to remain private until we can make API additions.
 145      */
 146     private PropertyChangeListener createPropertyChangeListener(JComponent c) {
 147         if (sharedPropertyChangedListener == null) {
 148             sharedPropertyChangedListener = new PropertyChangeHandler();
 149         }
 150         return sharedPropertyChangedListener;
 151     }
 152 
 153     public void paint(Graphics g, JComponent c) {
 154         Font font = c.getFont();
 155         FontMetrics metrics = SwingUtilities2.getFontMetrics(c, g, font);
 156         Dimension size = c.getSize();
 157 
 158         g.setColor(c.getForeground());
 159         // fix for bug 4153892
 160         String tipText = ((JToolTip)c).getTipText();
 161         if (tipText == null) {
 162             tipText = "";
 163         }
 164 
 165         Insets insets = c.getInsets();
 166         Rectangle paintTextR = new Rectangle(
 167             insets.left + 3,
 168             insets.top,
 169             size.width - (insets.left + insets.right) - 6,
 170             size.height - (insets.top + insets.bottom));
 171         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 172         if (v != null) {
 173             v.paint(g, paintTextR);
 174         } else {
 175             g.setFont(font);
 176             SwingUtilities2.drawString(c, g, tipText, paintTextR.x,
 177                                   paintTextR.y + metrics.getAscent());
 178         }
 179     }
 180 
 181     public Dimension getPreferredSize(JComponent c) {
 182         Font font = c.getFont();
 183         FontMetrics fm = c.getFontMetrics(font);
 184         Insets insets = c.getInsets();
 185 
 186         Dimension prefSize = new Dimension(insets.left+insets.right,
 187                                            insets.top+insets.bottom);
 188         String text = ((JToolTip)c).getTipText();
 189 
 190         if ((text == null) || text.equals("")) {
 191             text = "";
 192         }
 193         else {
 194             View v = (c != null) ? (View) c.getClientProperty("html") : null;
 195             if (v != null) {
 196                 prefSize.width += (int) v.getPreferredSpan(View.X_AXIS) + 6;
 197                 prefSize.height += (int) v.getPreferredSpan(View.Y_AXIS);
 198             } else {
 199                 prefSize.width += SwingUtilities2.stringWidth(c,fm,text) + 6;
 200                 prefSize.height += fm.getHeight();
 201             }
 202         }
 203         return prefSize;
 204     }
 205 
 206     public Dimension getMinimumSize(JComponent c) {
 207         Dimension d = getPreferredSize(c);
 208         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 209         if (v != null) {
 210             d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
 211         }
 212         return d;
 213     }
 214 
 215     public Dimension getMaximumSize(JComponent c) {
 216         Dimension d = getPreferredSize(c);
 217         View v = (View) c.getClientProperty(BasicHTML.propertyKey);
 218         if (v != null) {
 219             d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
 220         }
 221         return d;
 222     }
 223 
 224     /**
 225      * Invoked when the <code>JCompoment</code> associated with the
 226      * <code>JToolTip</code> has changed, or at initialization time. This
 227      * should update any state dependent upon the <code>JComponent</code>.
 228      *
 229      * @param c the JToolTip the JComponent has changed on.
 230      */
 231     private void componentChanged(JComponent c) {
 232         JComponent comp = ((JToolTip)c).getComponent();
 233 
 234         if (comp != null && !(comp.isEnabled())) {
 235             // For better backward compatibility, only install inactive
 236             // properties if they are defined.
 237             if (UIManager.getBorder("ToolTip.borderInactive") != null) {
 238                 LookAndFeel.installBorder(c, "ToolTip.borderInactive");
 239             }
 240             else {
 241                 LookAndFeel.installBorder(c, "ToolTip.border");
 242             }
 243             if (UIManager.getColor("ToolTip.backgroundInactive") != null) {
 244                 LookAndFeel.installColors(c,"ToolTip.backgroundInactive",
 245                                           "ToolTip.foregroundInactive");
 246             }
 247             else {
 248                 LookAndFeel.installColors(c,"ToolTip.background",
 249                                           "ToolTip.foreground");
 250             }
 251         } else {
 252             LookAndFeel.installBorder(c, "ToolTip.border");
 253             LookAndFeel.installColors(c, "ToolTip.background",
 254                                       "ToolTip.foreground");
 255         }
 256     }
 257 
 258 
 259     private static class PropertyChangeHandler implements
 260                                  PropertyChangeListener {
 261         public void propertyChange(PropertyChangeEvent e) {
 262             String name = e.getPropertyName();
 263             if (name.equals("tiptext") || "foreground".equals(name)
 264                 || "font".equals(name) || SwingUtilities2.isScaleChanged(e)) {
 265                 // remove the old html view client property if one
 266                 // existed, and install a new one if the text installed
 267                 // into the JLabel is html source.
 268                 JToolTip tip = ((JToolTip) e.getSource());
 269                 String text = tip.getTipText();
 270                 BasicHTML.updateRenderer(tip, text);
 271             }
 272             else if ("component".equals(name)) {
 273                 JToolTip tip = ((JToolTip) e.getSource());
 274 
 275                 if (tip.getUI() instanceof BasicToolTipUI) {
 276                     ((BasicToolTipUI)tip.getUI()).componentChanged(tip);
 277                 }
 278             }
 279         }
 280     }
 281 }