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.UIDefaults;
  60: import javax.swing.UIManager;
  61: import javax.swing.border.Border;
  62: import javax.swing.plaf.ComponentUI;
  63: import javax.swing.plaf.UIResource;
  64: import javax.swing.plaf.basic.BasicToolTipUI;
  65: 
  66: /**
  67:  * A UI delegate for the {@link JToolTip} component.
  68:  */
  69: public class MetalToolTipUI
  70:   extends BasicToolTipUI
  71: {
  72:   /** 
  73:    * The amount of space between the tool tip text and the accelerator 
  74:    * description (if visible). 
  75:    */
  76:   public static final int padSpaceBetweenStrings = 12;
  77: 
  78:   /** The shared UI instance. */
  79:   private static MetalToolTipUI instance = null;
  80:   
  81:   /** A flag controlling the visibility of the accelerator (if there is one). */
  82:   private boolean isAcceleratorHidden;
  83:   
  84:   /** A string representing the accelerator key for the component. */
  85:   private String acceleratorString;
  86:   
  87:   /** 
  88:    * The delimiter for the accelerator string.
  89:    */
  90:   private String acceleratorDelimiter;
  91:   
  92:   /** The font for the accelerator string. */
  93:   private Font acceleratorFont;
  94:   
  95:   /** The color for the accelerator string. */
  96:   private Color acceleratorForeground;
  97:   
  98:   /** The active border. */
  99:   private Border activeBorder;
 100:   
 101:   /** The inactive border. */
 102:   private Border inactiveBorder;
 103:   
 104:   /**
 105:    * Constructs a new instance of <code>MetalToolTipUI</code>.
 106:    */
 107:   public MetalToolTipUI()
 108:   {
 109:     super();
 110:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 111:     activeBorder = defaults.getBorder("ToolTip.border");
 112:     inactiveBorder = defaults.getBorder("ToolTip.borderInactive");
 113:     isAcceleratorHidden = defaults.getBoolean("ToolTip.hideAccelerator");
 114:     acceleratorFont = defaults.getFont("MenuItem.acceleratorFont");
 115:     acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground");
 116:     acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter");
 117:   }
 118: 
 119:   /**
 120:    * Returns a shared instance of the <code>MetalToolTipUI</code> class.
 121:    * Although this UI delegate does maintain state information, there is never
 122:    * more than one tool tip visible, so it is OK to use a shared instance.
 123:    *
 124:    * @param component  the component (a {@link JToolTip}).
 125:    *
 126:    * @return A shared instance of the <code>MetalToolTipUI</code> class.
 127:    */
 128:   public static ComponentUI createUI(JComponent component)
 129:   {
 130:     if (instance == null)
 131:       instance = new MetalToolTipUI();
 132:     return instance;
 133:   }
 134:   
 135:   /**
 136:    * Returns a string representing the accelerator key (if there is one) for 
 137:    * the component that the tool tip belongs to.
 138:    * 
 139:    * @return A string representing the accelerator key.
 140:    */
 141:   public String getAcceleratorString()
 142:   {
 143:     return acceleratorString;   
 144:   }
 145:   
 146:   /**
 147:    * Installs the UI for the specified component (a {@link JToolTip}).
 148:    * 
 149:    * @param c  the {@link JToolTip} component.
 150:    */
 151:   public void installUI(JComponent c)
 152:   {
 153:     super.installUI(c);
 154:     Border existingBorder = c.getBorder();
 155:     if (existingBorder == null || existingBorder instanceof UIResource)
 156:       {
 157:         if (c.isEnabled())
 158:           c.setBorder(activeBorder);
 159:         else
 160:           c.setBorder(inactiveBorder);
 161:       }   
 162:   }
 163:   
 164:   /**
 165:    * Clears the defaults set in {@link #installUI(JComponent)}.
 166:    * 
 167:    * @param c  the component.
 168:    */
 169:   public void uninstallUI(JComponent c)
 170:   {
 171:     super.uninstallUI(c);
 172:     if (c.getBorder() instanceof UIResource)
 173:       c.setBorder(null);
 174:   }
 175:   
 176:   /**
 177:    * Returns <code>true</code> if the accelerator string is hidden, and
 178:    * <code>false</code> otherwise.  This setting is controlled by the
 179:    * <code>ToolTip.hideAccelerator</code> entry in the UI defaults table.
 180:    *
 181:    * @return A boolean.
 182:    */
 183:   protected boolean isAcceleratorHidden()
 184:   {
 185:     return isAcceleratorHidden;
 186:   }
 187:   
 188:   /**
 189:    * Returns the preferred size for the {@link JToolTip} component.
 190:    * 
 191:    * @param c  the component (a {@link JToolTip}).
 192:    * 
 193:    * @return The preferred size.
 194:    */
 195:   public Dimension getPreferredSize(JComponent c)
 196:   {
 197:     if (isAcceleratorHidden())
 198:       return super.getPreferredSize(c);
 199:     else
 200:       {
 201:         Insets insets = c.getInsets();
 202:         JToolTip tt = (JToolTip) c;
 203:         String tipText = tt.getTipText();
 204:         if (tipText != null)
 205:           {
 206:             FontMetrics fm = c.getFontMetrics(c.getFont());
 207:             int prefH = fm.getHeight() + insets.top + insets.bottom;
 208:             int prefW = fm.stringWidth(tipText) + insets.left + insets.right;
 209: 
 210:             // this seems to be the first opportunity we have to get the 
 211:             // accelerator string from the component (if it has one)
 212:             acceleratorString = fetchAcceleratorString(c);
 213:             if (acceleratorString != null)
 214:               {
 215:                 prefW += padSpaceBetweenStrings;
 216:                 fm = c.getFontMetrics(acceleratorFont);
 217:                 prefW += fm.stringWidth(acceleratorString);                
 218:               }
 219:             return new Dimension(prefW, prefH);  
 220:           }
 221:         else return new Dimension(0, 0);
 222:       }
 223:   }
 224:   
 225:   /**
 226:    * Paints the tool tip.
 227:    * 
 228:    * @param g  the graphics context.
 229:    * @param c  the {@link JToolTip} component.
 230:    */
 231:   public void paint(Graphics g, JComponent c)
 232:   {
 233:     JToolTip tip = (JToolTip) c;
 234: 
 235:     String text = tip.getTipText();
 236:     Toolkit t = tip.getToolkit();
 237:     if (text == null)
 238:       return;
 239: 
 240:     Rectangle vr = new Rectangle();
 241:     vr = SwingUtilities.calculateInnerArea(tip, vr);
 242:     Rectangle ir = new Rectangle();
 243:     Rectangle tr = new Rectangle();
 244:     FontMetrics fm = t.getFontMetrics(tip.getFont());
 245:     int ascent = fm.getAscent();
 246:     SwingUtilities.layoutCompoundLabel(tip, fm, text, null, 
 247:             SwingConstants.CENTER, SwingConstants.LEFT,
 248:             SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0);
 249:     Color saved = g.getColor();
 250:     g.setColor(Color.BLACK);
 251: 
 252:     g.drawString(text, vr.x, vr.y + ascent); 
 253:     
 254:     // paint accelerator
 255:     if (acceleratorString != null)
 256:       {
 257:         g.setFont(acceleratorFont);
 258:         g.setColor(acceleratorForeground);
 259:         fm = t.getFontMetrics(acceleratorFont);
 260:         int width = fm.stringWidth(acceleratorString);
 261:         g.drawString(acceleratorString, vr.x + vr.width - width - padSpaceBetweenStrings/2, 
 262:                 vr.y + vr.height - fm.getDescent());
 263:       }
 264: 
 265:     g.setColor(saved);   
 266:   }
 267:   
 268:   /**
 269:    * Returns a string representing the accelerator for the component, or 
 270:    * <code>null</code> if the component has no accelerator.
 271:    * 
 272:    * @param c  the component.
 273:    * 
 274:    * @return A string representing the accelerator (possibly 
 275:    *         <code>null</code>).
 276:    */
 277:   private String fetchAcceleratorString(JComponent c)
 278:   {
 279:     String result = null;
 280:     if (c instanceof JToolTip)
 281:       {
 282:         JToolTip toolTip = (JToolTip) c;
 283:         JComponent component = toolTip.getComponent();
 284:         KeyStroke ks = null;
 285:         int mne = 0;
 286:         if (component instanceof JMenuItem)
 287:           {
 288:             JMenuItem item = (JMenuItem) component;
 289:             ks = item.getAccelerator();
 290:             if (ks == null)
 291:                 mne = item.getMnemonic();
 292:           }
 293:         else if (component instanceof AbstractButton)
 294:           {
 295:             AbstractButton button = (AbstractButton) component;
 296:             mne = button.getMnemonic();
 297:           }
 298:         if (mne > 0)
 299:           ks = KeyStroke.getKeyStroke(Character.toUpperCase((char) mne), 
 300:                 InputEvent.ALT_MASK, false);
 301:         if (ks != null)
 302:           result = acceleratorToString(ks);
 303:       }
 304:     return result;
 305:   }
 306:   
 307:   /**
 308:    * Returns a string representing an accelerator.
 309:    * 
 310:    * @param accelerator  the accelerator (<code>null</code> not permitted).
 311:    * 
 312:    * @return A string representing an accelerator.
 313:    */
 314:   private String acceleratorToString(KeyStroke accelerator)
 315:   {
 316:     // convert keystroke into string format
 317:     String modifiersText = "";
 318:     int modifiers = accelerator.getModifiers();
 319:     char keyChar = accelerator.getKeyChar();
 320:     int keyCode = accelerator.getKeyCode();
 321:     
 322:     if (modifiers != 0)
 323:       modifiersText = KeyEvent.getKeyModifiersText(modifiers) 
 324:           + acceleratorDelimiter;
 325: 
 326:     if (keyCode == KeyEvent.VK_UNDEFINED)
 327:       return modifiersText + keyChar;
 328:     else
 329:       return modifiersText + KeyEvent.getKeyText(keyCode);
 330:   }
 331: 
 332: }