Source for javax.swing.plaf.metal.MetalToolTipUI

   1: /* MetalToolTipUI.java
   2:    Copyright (C) 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.metal;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Dimension;
  43: import java.awt.Font;
  44: import java.awt.FontMetrics;
  45: import java.awt.Graphics;
  46: import java.awt.Insets;
  47: import java.awt.Rectangle;
  48: import java.awt.Toolkit;
  49: import java.awt.event.InputEvent;
  50: import java.awt.event.KeyEvent;
  51: 
  52: import javax.swing.AbstractButton;
  53: import javax.swing.JComponent;
  54: import javax.swing.JMenuItem;
  55: import javax.swing.JToolTip;
  56: import javax.swing.KeyStroke;
  57: import javax.swing.SwingConstants;
  58: import javax.swing.SwingUtilities;
  59: import javax.swing.UIManager;
  60: import javax.swing.border.Border;
  61: import javax.swing.plaf.ComponentUI;
  62: import javax.swing.plaf.UIResource;
  63: import javax.swing.plaf.basic.BasicToolTipUI;
  64: 
  65: /**
  66:  * A UI delegate for the {@link JToolTip} component.
  67:  */
  68: public class MetalToolTipUI
  69:   extends BasicToolTipUI
  70: {
  71:   /** 
  72:    * The amount of space between the tool tip text and the accelerator 
  73:    * description (if visible). 
  74:    */
  75:   public static final int padSpaceBetweenStrings = 12;
  76: 
  77:   /** The shared UI instance. */
  78:   private static MetalToolTipUI instance;
  79:   
  80:   /** A flag controlling the visibility of the accelerator (if there is one). */
  81:   private boolean isAcceleratorHidden;
  82:   
  83:   /** A string representing the accelerator key for the component. */
  84:   private String acceleratorString;
  85:   
  86:   /** 
  87:    * The delimiter for the accelerator string.
  88:    */
  89:   private String acceleratorDelimiter;
  90:   
  91:   /** The font for the accelerator string. */
  92:   private Font acceleratorFont;
  93:   
  94:   /** The color for the accelerator string. */
  95:   private Color acceleratorForeground;
  96:   
  97:   /** The active border. */
  98:   private Border activeBorder;
  99:   
 100:   /** The inactive border. */
 101:   private Border inactiveBorder;
 102:   
 103:   /**
 104:    * Constructs a new instance of <code>MetalToolTipUI</code>.
 105:    */
 106:   public MetalToolTipUI()
 107:   {
 108:     super();
 109:     activeBorder = UIManager.getBorder("ToolTip.border");
 110:     inactiveBorder = UIManager.getBorder("ToolTip.borderInactive");
 111:     isAcceleratorHidden = UIManager.getBoolean("ToolTip.hideAccelerator");
 112:     acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont");
 113:     acceleratorForeground = UIManager.getColor("MenuItem.acceleratorForeground");
 114:     acceleratorDelimiter = UIManager.getString("MenuItem.acceleratorDelimiter");
 115:   }
 116: 
 117:   /**
 118:    * Returns a shared instance of the <code>MetalToolTipUI</code> class.
 119:    * Although this UI delegate does maintain state information, there is never
 120:    * more than one tool tip visible, so it is OK to use a shared instance.
 121:    *
 122:    * @param component  the component (a {@link JToolTip}).
 123:    *
 124:    * @return A shared instance of the <code>MetalToolTipUI</code> class.
 125:    */
 126:   public static ComponentUI createUI(JComponent component)
 127:   {
 128:     if (instance == null)
 129:       instance = new MetalToolTipUI();
 130:     return instance;
 131:   }
 132:   
 133:   /**
 134:    * Returns a string representing the accelerator key (if there is one) for 
 135:    * the component that the tool tip belongs to.
 136:    * 
 137:    * @return A string representing the accelerator key.
 138:    */
 139:   public String getAcceleratorString()
 140:   {
 141:     return acceleratorString;   
 142:   }
 143:   
 144:   /**
 145:    * Installs the UI for the specified component (a {@link JToolTip}).
 146:    * 
 147:    * @param c  the {@link JToolTip} component.
 148:    */
 149:   public void installUI(JComponent c)
 150:   {
 151:     super.installUI(c);
 152:     Border existingBorder = c.getBorder();
 153:     if (existingBorder == null || existingBorder instanceof UIResource)
 154:       {
 155:         if (c.isEnabled())
 156:           c.setBorder(activeBorder);
 157:         else
 158:           c.setBorder(inactiveBorder);
 159:       }   
 160:   }
 161:   
 162:   /**
 163:    * Clears the defaults set in {@link #installUI(JComponent)}.
 164:    * 
 165:    * @param c  the component.
 166:    */
 167:   public void uninstallUI(JComponent c)
 168:   {
 169:     super.uninstallUI(c);
 170:     if (c.getBorder() instanceof UIResource)
 171:       c.setBorder(null);
 172:   }
 173:   
 174:   /**
 175:    * Returns <code>true</code> if the accelerator string is hidden, and
 176:    * <code>false</code> otherwise.  This setting is controlled by the
 177:    * <code>ToolTip.hideAccelerator</code> entry in the UI defaults table.
 178:    *
 179:    * @return A boolean.
 180:    */
 181:   protected boolean isAcceleratorHidden()
 182:   {
 183:     return isAcceleratorHidden;
 184:   }
 185:   
 186:   /**
 187:    * Returns the preferred size for the {@link JToolTip} component.
 188:    * 
 189:    * @param c  the component (a {@link JToolTip}).
 190:    * 
 191:    * @return The preferred size.
 192:    */
 193:   public Dimension getPreferredSize(JComponent c)
 194:   {
 195:     if (isAcceleratorHidden())
 196:       return super.getPreferredSize(c);
 197:     else
 198:       {
 199:         Insets insets = c.getInsets();
 200:         JToolTip tt = (JToolTip) c;
 201:         String tipText = tt.getTipText();
 202:         if (tipText != null)
 203:           {
 204:             FontMetrics fm = c.getFontMetrics(c.getFont());
 205:             int prefH = fm.getHeight() + insets.top + insets.bottom;
 206:             int prefW = fm.stringWidth(tipText) + insets.left + insets.right;
 207: 
 208:             // this seems to be the first opportunity we have to get the 
 209:             // accelerator string from the component (if it has one)
 210:             acceleratorString = fetchAcceleratorString(c);
 211:             if (acceleratorString != null)
 212:               {
 213:                 prefW += padSpaceBetweenStrings;
 214:                 fm = c.getFontMetrics(acceleratorFont);
 215:                 prefW += fm.stringWidth(acceleratorString);                
 216:               }
 217:             return new Dimension(prefW, prefH);  
 218:           }
 219:         else return new Dimension(0, 0);
 220:       }
 221:   }
 222:   
 223:   /**
 224:    * Paints the tool tip.
 225:    * 
 226:    * @param g  the graphics context.
 227:    * @param c  the {@link JToolTip} component.
 228:    */
 229:   public void paint(Graphics g, JComponent c)
 230:   {
 231:     JToolTip tip = (JToolTip) c;
 232: 
 233:     String text = tip.getTipText();
 234:     Toolkit t = tip.getToolkit();
 235:     if (text == null)
 236:       return;
 237: 
 238:     Rectangle vr = new Rectangle();
 239:     vr = SwingUtilities.calculateInnerArea(tip, vr);
 240:     Rectangle ir = new Rectangle();
 241:     Rectangle tr = new Rectangle();
 242:     FontMetrics fm = t.getFontMetrics(tip.getFont());
 243:     int ascent = fm.getAscent();
 244:     SwingUtilities.layoutCompoundLabel(tip, fm, text, null, 
 245:             SwingConstants.CENTER, SwingConstants.LEFT,
 246:             SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0);
 247:     Color saved = g.getColor();
 248:     g.setColor(Color.BLACK);
 249: 
 250:     g.drawString(text, vr.x, vr.y + ascent); 
 251:     
 252:     // paint accelerator
 253:     if (acceleratorString != null)
 254:       {
 255:         g.setFont(acceleratorFont);
 256:         g.setColor(acceleratorForeground);
 257:         fm = t.getFontMetrics(acceleratorFont);
 258:         int width = fm.stringWidth(acceleratorString);
 259:         g.drawString(acceleratorString, vr.x + vr.width - width 
 260:             - padSpaceBetweenStrings / 2, vr.y + vr.height - fm.getDescent());
 261:       }
 262: 
 263:     g.setColor(saved);   
 264:   }
 265:   
 266:   /**
 267:    * Returns a string representing the accelerator for the component, or 
 268:    * <code>null</code> if the component has no accelerator.
 269:    * 
 270:    * @param c  the component.
 271:    * 
 272:    * @return A string representing the accelerator (possibly 
 273:    *         <code>null</code>).
 274:    */
 275:   private String fetchAcceleratorString(JComponent c)
 276:   {
 277:     String result = null;
 278:     if (c instanceof JToolTip)
 279:       {
 280:         JToolTip toolTip = (JToolTip) c;
 281:         JComponent component = toolTip.getComponent();
 282:         KeyStroke ks = null;
 283:         int mne = 0;
 284:         if (component instanceof JMenuItem)
 285:           {
 286:             JMenuItem item = (JMenuItem) component;
 287:             ks = item.getAccelerator();
 288:             if (ks == null)
 289:                 mne = item.getMnemonic();
 290:           }
 291:         else if (component instanceof AbstractButton)
 292:           {
 293:             AbstractButton button = (AbstractButton) component;
 294:             mne = button.getMnemonic();
 295:           }
 296:         if (mne > 0)
 297:           ks = KeyStroke.getKeyStroke(Character.toUpperCase((char) mne), 
 298:                 InputEvent.ALT_MASK, false);
 299:         if (ks != null)
 300:           result = acceleratorToString(ks);
 301:       }
 302:     return result;
 303:   }
 304:   
 305:   /**
 306:    * Returns a string representing an accelerator.
 307:    * 
 308:    * @param accelerator  the accelerator (<code>null</code> not permitted).
 309:    * 
 310:    * @return A string representing an accelerator.
 311:    */
 312:   private String acceleratorToString(KeyStroke accelerator)
 313:   {
 314:     // convert keystroke into string format
 315:     String modifiersText = "";
 316:     int modifiers = accelerator.getModifiers();
 317:     char keyChar = accelerator.getKeyChar();
 318:     int keyCode = accelerator.getKeyCode();
 319:     
 320:     if (modifiers != 0)
 321:       modifiersText = KeyEvent.getKeyModifiersText(modifiers) 
 322:           + acceleratorDelimiter;
 323: 
 324:     if (keyCode == KeyEvent.VK_UNDEFINED)
 325:       return modifiersText + keyChar;
 326:     else
 327:       return modifiersText + KeyEvent.getKeyText(keyCode);
 328:   }
 329: 
 330: }