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 private TextUIDrawing textUIDrawing; 69 70 /** 71 * Constructs an instance of the {@code MetalToolTipUI}. 72 */ 73 public MetalToolTipUI() { 74 super(); 75 } 76 77 /** 78 * Returns an instance of the {@code MetalToolTipUI}. 79 * 80 * @param c a component 81 * @return an instance of the {@code MetalToolTipUI}. 82 */ 83 public static ComponentUI createUI(JComponent c) { 84 return sharedInstance; 85 } 86 87 public void installUI(JComponent c) { 88 super.installUI(c); 89 tip = (JToolTip)c; 90 Font f = c.getFont(); 91 smallFont = new Font( f.getName(), f.getStyle(), f.getSize() - 2 ); 92 acceleratorDelimiter = UIManager.getString( "MenuItem.acceleratorDelimiter" ); 93 if ( acceleratorDelimiter == null ) { acceleratorDelimiter = "-"; } 94 } 95 96 public void uninstallUI(JComponent c) { 97 super.uninstallUI(c); 98 tip = null; 99 } 100 101 @Override 102 protected void installDefaults(JComponent c) { 103 super.installDefaults(c); 104 if (textUIDrawing != SwingUtilities2.DEFAULT_UI_TEXT_DRAWING 105 && textUIDrawing instanceof UIResource) { 106 textUIDrawing = SwingUtilities2.DEFAULT_UI_TEXT_DRAWING; 107 } 108 } 109 110 @Override 111 protected void uninstallDefaults(JComponent c) { 112 super.uninstallDefaults(c); 113 if (textUIDrawing instanceof UIResource) { 114 textUIDrawing = null; 115 } 116 } 117 118 public void paint(Graphics g, JComponent c) { 119 JToolTip tip = (JToolTip)c; 120 Font font = c.getFont(); 121 FontMetrics metrics = SwingUtilities2.getFontMetrics(c, g, font); 122 Dimension size = c.getSize(); 123 int accelBL; 124 125 g.setColor(c.getForeground()); 126 // fix for bug 4153892 127 String tipText = tip.getTipText(); 128 if (tipText == null) { 129 tipText = ""; 130 } 131 132 String accelString = getAcceleratorString(tip); 133 FontMetrics accelMetrics = SwingUtilities2.getFontMetrics(c, g, smallFont); 134 int accelSpacing = calcAccelSpacing(c, accelMetrics, accelString); 135 136 Insets insets = tip.getInsets(); 137 Rectangle paintTextR = new Rectangle( 138 insets.left + 3, 139 insets.top, 140 size.width - (insets.left + insets.right) - 6 - accelSpacing, 141 size.height - (insets.top + insets.bottom)); 142 View v = (View) c.getClientProperty(BasicHTML.propertyKey); 143 if (v != null) { 144 v.paint(g, paintTextR); 145 accelBL = BasicHTML.getHTMLBaseline(v, paintTextR.width, 146 paintTextR.height); 147 } else { 148 g.setFont(font); 149 textUIDrawing.drawString(tip, g, tipText, paintTextR.x, 150 paintTextR.y + metrics.getAscent()); 151 accelBL = metrics.getAscent(); 152 } 153 154 if (!accelString.equals("")) { 155 g.setFont(smallFont); 156 g.setColor( MetalLookAndFeel.getPrimaryControlDarkShadow() ); 157 textUIDrawing.drawString(tip, g, accelString, 158 tip.getWidth() - 1 - insets.right 159 - accelSpacing 160 + padSpaceBetweenStrings 161 - 3, 162 paintTextR.y + accelBL); 163 } 164 } 165 166 private int calcAccelSpacing(JComponent c, FontMetrics fm, String accel) { 167 return accel.equals("") 168 ? 0 169 : padSpaceBetweenStrings + 170 textUIDrawing.getStringWidth(c, fm, accel); 171 } 172 173 public Dimension getPreferredSize(JComponent c) { 174 Dimension d = super.getPreferredSize(c); 175 176 String key = getAcceleratorString((JToolTip)c); 177 if (!(key.equals(""))) { 178 d.width += calcAccelSpacing(c, c.getFontMetrics(smallFont), key); 179 } 180 return d; 181 } 182 183 /** 184 * If the accelerator is hidden, the method returns {@code true}, 185 * otherwise, returns {@code false}. 186 * 187 * @return {@code true} if the accelerator is hidden. 188 */ 189 protected boolean isAcceleratorHidden() { 190 Boolean b = (Boolean)UIManager.get("ToolTip.hideAccelerator"); 191 return b != null && b.booleanValue(); 192 } 193 194 private String getAcceleratorString(JToolTip tip) { 195 this.tip = tip; 196 197 String retValue = getAcceleratorString(); 198 199 this.tip = null; 200 return retValue; 201 } 202 203 /** 204 * Returns the accelerator string. 205 * 206 * @return the accelerator string. 207 */ 208 // NOTE: This requires the tip field to be set before this is invoked. 209 // As MetalToolTipUI is shared between all JToolTips the tip field is 210 // set appropriately before this is invoked. Unfortunately this means 211 // that subclasses that randomly invoke this method will see varying 212 // results. If this becomes an issue, MetalToolTipUI should no longer be 213 // shared. 214 public String getAcceleratorString() { 215 if (tip == null || isAcceleratorHidden()) { 216 return ""; 217 } 218 JComponent comp = tip.getComponent(); 219 if (!(comp instanceof AbstractButton)) { 220 return ""; 221 } 222 223 KeyStroke[] keys = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).keys(); 224 if (keys == null) { 225 return ""; 226 } 227 228 String controlKeyStr = ""; 229 230 for (int i = 0; i < keys.length; i++) { 231 int mod = keys[i].getModifiers(); 232 controlKeyStr = KeyEvent.getKeyModifiersText(mod) + 233 acceleratorDelimiter + 234 KeyEvent.getKeyText(keys[i].getKeyCode()); 235 break; 236 } 237 238 return controlKeyStr; 239 } 240 241 }