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 26 package javax.swing.plaf.metal; 27 28 import sun.swing.SwingUtilities2; 29 import java.awt.*; 30 import java.awt.event.*; 31 import javax.swing.*; 32 import javax.swing.BorderFactory; 33 import javax.swing.border.Border; 34 import javax.swing.plaf.*; 35 import javax.swing.plaf.basic.BasicToolTipUI; 36 import javax.swing.plaf.basic.BasicHTML; 37 import 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™ 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 55 public 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 public String getAcceleratorString() { 196 if (tip == null || isAcceleratorHidden()) { 197 return ""; 198 } 199 JComponent comp = tip.getComponent(); 200 if (!(comp instanceof AbstractButton)) { 201 return ""; 202 } 203 204 KeyStroke[] keys = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).keys(); 205 if (keys == null) { 206 return ""; 207 } 208 209 String controlKeyStr = ""; 210 211 for (int i = 0; i < keys.length; i++) { 212 int mod = keys[i].getModifiers(); 213 controlKeyStr = KeyEvent.getKeyModifiersText(mod) + 214 acceleratorDelimiter + 215 KeyEvent.getKeyText(keys[i].getKeyCode()); 216 break; 217 } 218 219 return controlKeyStr; 220 } 221 222 }