1/*
2 * Copyright (c) 1998, 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
26package javax.swing.plaf.metal;
27
28import sun.swing.SwingUtilities2;
29import java.awt.*;
30import java.awt.event.*;
31import javax.swing.*;
32import javax.swing.BorderFactory;
33import javax.swing.border.Border;
34import javax.swing.plaf.*;
35import javax.swing.plaf.basic.BasicToolTipUI;
36import javax.swing.plaf.basic.BasicHTML;
37import javax.swing.text.View;
38
39
40/**
41 * A Metal L&F extension of BasicToolTipUI.
42 * <p>
43 * <strong>Warning:</strong>
44 * Serialized objects of this class will not be compatible with
45 * future Swing releases. The current serialization support is
46 * appropriate for short term storage or RMI between applications running
47 * the same version of Swing.  As of 1.4, support for long term storage
48 * of all JavaBeans&trade;
49 * has been added to the <code>java.beans</code> package.
50 * Please see {@link java.beans.XMLEncoder}.
51 *
52 * @author Steve Wilson
53 */
54@SuppressWarnings("serial") // Same-version serialization only
55public class MetalToolTipUI extends BasicToolTipUI {
56
57    static MetalToolTipUI sharedInstance = new MetalToolTipUI();
58    private Font smallFont;
59    // Refer to note in getAcceleratorString about this field.
60    private JToolTip tip;
61
62    /**
63     * The space between strings.
64     */
65    public static final int padSpaceBetweenStrings = 12;
66    private String acceleratorDelimiter;
67
68    /**
69     * Constructs an instance of the {@code MetalToolTipUI}.
70     */
71    public MetalToolTipUI() {
72        super();
73    }
74
75    /**
76     * Returns an instance of the {@code MetalToolTipUI}.
77     *
78     * @param c a component
79     * @return an instance of the {@code MetalToolTipUI}.
80     */
81    public static ComponentUI createUI(JComponent c) {
82        return sharedInstance;
83    }
84
85    public void installUI(JComponent c) {
86        super.installUI(c);
87        tip = (JToolTip)c;
88        Font f = c.getFont();
89        smallFont = new Font( f.getName(), f.getStyle(), f.getSize() - 2 );
90        acceleratorDelimiter = UIManager.getString( "MenuItem.acceleratorDelimiter" );
91        if ( acceleratorDelimiter == null ) { acceleratorDelimiter = "-"; }
92    }
93
94    public void uninstallUI(JComponent c) {
95        super.uninstallUI(c);
96        tip = null;
97    }
98
99    public void paint(Graphics g, JComponent c) {
100        JToolTip tip = (JToolTip)c;
101        Font font = c.getFont();
102        FontMetrics metrics = SwingUtilities2.getFontMetrics(c, g, font);
103        Dimension size = c.getSize();
104        int accelBL;
105
106        g.setColor(c.getForeground());
107        // fix for bug 4153892
108        String tipText = tip.getTipText();
109        if (tipText == null) {
110            tipText = "";
111        }
112
113        String accelString = getAcceleratorString(tip);
114        FontMetrics accelMetrics = SwingUtilities2.getFontMetrics(c, g, smallFont);
115        int accelSpacing = calcAccelSpacing(c, accelMetrics, accelString);
116
117        Insets insets = tip.getInsets();
118        Rectangle paintTextR = new Rectangle(
119            insets.left + 3,
120            insets.top,
121            size.width - (insets.left + insets.right) - 6 - accelSpacing,
122            size.height - (insets.top + insets.bottom));
123        View v = (View) c.getClientProperty(BasicHTML.propertyKey);
124        if (v != null) {
125            v.paint(g, paintTextR);
126            accelBL = BasicHTML.getHTMLBaseline(v, paintTextR.width,
127                                                  paintTextR.height);
128        } else {
129            g.setFont(font);
130            SwingUtilities2.drawString(tip, g, tipText, paintTextR.x,
131                                  paintTextR.y + metrics.getAscent());
132            accelBL = metrics.getAscent();
133        }
134
135        if (!accelString.equals("")) {
136            g.setFont(smallFont);
137            g.setColor( MetalLookAndFeel.getPrimaryControlDarkShadow() );
138            SwingUtilities2.drawString(tip, g, accelString,
139                                       tip.getWidth() - 1 - insets.right
140                                           - accelSpacing
141                                           + padSpaceBetweenStrings
142                                           - 3,
143                                       paintTextR.y + accelBL);
144        }
145    }
146
147    private int calcAccelSpacing(JComponent c, FontMetrics fm, String accel) {
148        return accel.equals("")
149               ? 0
150               : padSpaceBetweenStrings +
151                 SwingUtilities2.stringWidth(c, fm, accel);
152    }
153
154    public Dimension getPreferredSize(JComponent c) {
155        Dimension d = super.getPreferredSize(c);
156
157        String key = getAcceleratorString((JToolTip)c);
158        if (!(key.equals(""))) {
159            d.width += calcAccelSpacing(c, c.getFontMetrics(smallFont), key);
160        }
161        return d;
162    }
163
164    /**
165     * If the accelerator is hidden, the method returns {@code true},
166     * otherwise, returns {@code false}.
167     *
168     * @return {@code true} if the accelerator is hidden.
169     */
170    protected boolean isAcceleratorHidden() {
171        Boolean b = (Boolean)UIManager.get("ToolTip.hideAccelerator");
172        return b != null && b.booleanValue();
173    }
174
175    private String getAcceleratorString(JToolTip tip) {
176        this.tip = tip;
177
178        String retValue = getAcceleratorString();
179
180        this.tip = null;
181        return retValue;
182    }
183
184    /**
185     * Returns the accelerator string.
186     *
187     * @return the accelerator string.
188     */
189    // NOTE: This requires the tip field to be set before this is invoked.
190    // As MetalToolTipUI is shared between all JToolTips the tip field is
191    // set appropriately before this is invoked. Unfortunately this means
192    // that subclasses that randomly invoke this method will see varying
193    // results. If this becomes an issue, MetalToolTipUI should no longer be
194    // shared.
195    @SuppressWarnings("deprecation")
196    public String getAcceleratorString() {
197        if (tip == null || isAcceleratorHidden()) {
198            return "";
199        }
200        JComponent comp = tip.getComponent();
201        if (!(comp instanceof AbstractButton)) {
202            return "";
203        }
204
205        KeyStroke[] keys = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).keys();
206        if (keys == null) {
207            return "";
208        }
209
210        String controlKeyStr = "";
211
212        for (int i = 0; i < keys.length; i++) {
213            int mod = keys[i].getModifiers();
214            controlKeyStr = KeyEvent.getKeyModifiersText(mod) +
215                            acceleratorDelimiter +
216                            KeyEvent.getKeyText(keys[i].getKeyCode());
217            break;
218        }
219
220        return controlKeyStr;
221    }
222
223}
224