Source for javax.swing.plaf.basic.BasicLabelUI

   1: /* BasicLabelUI.java
   2:  Copyright (C) 2002, 2004, 2006, 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: package javax.swing.plaf.basic;
  39: 
  40: import java.awt.Component;
  41: import java.awt.Dimension;
  42: import java.awt.Font;
  43: import java.awt.FontMetrics;
  44: import java.awt.Graphics;
  45: import java.awt.Insets;
  46: import java.awt.Rectangle;
  47: import java.awt.Toolkit;
  48: import java.awt.event.ActionEvent;
  49: import java.awt.event.KeyEvent;
  50: import java.beans.PropertyChangeEvent;
  51: import java.beans.PropertyChangeListener;
  52: 
  53: import javax.swing.AbstractAction;
  54: import javax.swing.ActionMap;
  55: import javax.swing.Icon;
  56: import javax.swing.InputMap;
  57: import javax.swing.JComponent;
  58: import javax.swing.JLabel;
  59: import javax.swing.KeyStroke;
  60: import javax.swing.LookAndFeel;
  61: import javax.swing.SwingUtilities;
  62: import javax.swing.plaf.ComponentUI;
  63: import javax.swing.plaf.LabelUI;
  64: import javax.swing.text.View;
  65: 
  66: /**
  67:  * This is the Basic Look and Feel class for the JLabel.  One BasicLabelUI
  68:  * object is used to paint all JLabels that utilize the Basic Look and Feel.
  69:  */
  70: public class BasicLabelUI extends LabelUI implements PropertyChangeListener
  71: {
  72:   /** The labelUI that is shared by all labels. */
  73:   protected static BasicLabelUI labelUI;
  74: 
  75:   /**
  76:    * These fields hold the rectangles for the whole label,
  77:    * the icon and the text.
  78:    */
  79:   private Rectangle vr;
  80:   private Rectangle ir;
  81:   private Rectangle tr;
  82: 
  83:   /**
  84:    * A cached Insets object for reuse in the label layout methods.
  85:    */
  86:   private Insets cachedInsets;
  87: 
  88:   /**
  89:    * Creates a new BasicLabelUI object.
  90:    */
  91:   public BasicLabelUI()
  92:   {
  93:     super();
  94:     vr = new Rectangle();
  95:     ir = new Rectangle();
  96:     tr = new Rectangle();
  97:   }
  98: 
  99:   /**
 100:    * Creates and returns a UI for the label. Since one UI is shared by  all
 101:    * labels, this means creating only if necessary and returning the  shared
 102:    * UI.
 103:    *
 104:    * @param c The {@link JComponent} that a UI is being created for.
 105:    *
 106:    * @return A label UI for the Basic Look and Feel.
 107:    */
 108:   public static ComponentUI createUI(JComponent c)
 109:   {
 110:     if (labelUI == null)
 111:       labelUI = new BasicLabelUI();
 112:     return labelUI;
 113:   }
 114: 
 115:   /**
 116:    * Returns the preferred size of this component as calculated by the
 117:    * {@link #layoutCL(JLabel, FontMetrics, String, Icon, Rectangle, Rectangle,
 118:    * Rectangle)} method.
 119:    *
 120:    * @param c This {@link JComponent} to get a preferred size for.
 121:    *
 122:    * @return The preferred size.
 123:    */
 124:   public Dimension getPreferredSize(JComponent c)
 125:   {
 126:     JLabel lab = (JLabel) c;
 127:     Insets insets = lab.getInsets();
 128:     int insetsX = insets.left + insets.right;
 129:     int insetsY = insets.top + insets.bottom;
 130:     Icon icon = lab.getIcon();
 131:     String text = lab.getText();
 132:     Dimension ret;
 133:     if (icon == null && text == null)
 134:       ret = new Dimension(insetsX, insetsY);
 135:     else if (icon != null && text == null)
 136:       ret = new Dimension(icon.getIconWidth() + insetsX,
 137:                           icon.getIconHeight() + insetsY);
 138:     else
 139:       {
 140:         FontMetrics fm = getFontMetrics(lab);
 141:         ir.x = 0;
 142:         ir.y = 0;
 143:         ir.width = 0;
 144:         ir.height = 0;
 145:         tr.x = 0;
 146:         tr.y = 0;
 147:         tr.width = 0;
 148:         tr.height = 0;
 149:         vr.x = 0;
 150:         vr.y = 0;
 151:         vr.width = Short.MAX_VALUE;
 152:         vr.height = Short.MAX_VALUE;
 153:         layoutCL(lab, fm, text, icon, vr, ir, tr);
 154:         Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width,
 155:                                                    tr.height, ir);
 156:         ret = new Dimension(cr.width + insetsX, cr.height + insetsY);
 157:       }
 158:     return ret;
 159:   }
 160: 
 161:   /**
 162:    * This method returns the minimum size of the {@link JComponent} given. If
 163:    * this method returns null, then it is up to the Layout Manager to give
 164:    * this component a minimum size.
 165:    *
 166:    * @param c The {@link JComponent} to get a minimum size for.
 167:    *
 168:    * @return The minimum size.
 169:    */
 170:   public Dimension getMinimumSize(JComponent c)
 171:   {
 172:     return getPreferredSize(c);
 173:   }
 174: 
 175:   /**
 176:    * This method returns the maximum size of the {@link JComponent} given. If
 177:    * this method returns null, then it is up to the Layout Manager to give
 178:    * this component a maximum size.
 179:    *
 180:    * @param c The {@link JComponent} to get a maximum size for.
 181:    *
 182:    * @return The maximum size.
 183:    */
 184:   public Dimension getMaximumSize(JComponent c)
 185:   {
 186:     return getPreferredSize(c);
 187:   }
 188: 
 189:   /**
 190:    * The method that paints the label according to its current state.
 191:    *
 192:    * @param g The {@link Graphics} object to paint with.
 193:    * @param c The {@link JComponent} to paint.
 194:    */
 195:   public void paint(Graphics g, JComponent c)
 196:   {
 197:     JLabel b = (JLabel) c;
 198:     Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
 199:     String text = b.getText();
 200:     if (icon != null || (text != null && ! text.equals("")))
 201:       {
 202:         FontMetrics fm = getFontMetrics(b);
 203:         Insets i = c.getInsets(cachedInsets);
 204:         vr.x = i.left;
 205:         vr.y = i.right;
 206:         vr.width = c.getWidth() - i.left - i.right;
 207:         vr.height = c.getHeight() - i.top - i.bottom;
 208:         ir.x = 0;
 209:         ir.y = 0;
 210:         ir.width = 0;
 211:         ir.height = 0;
 212:         tr.x = 0;
 213:         tr.y = 0;
 214:         tr.width = 0;
 215:         tr.height = 0;
 216: 
 217:         text = layoutCL(b, fm, text, icon, vr, ir, tr);
 218: 
 219:         if (icon != null)
 220:           icon.paintIcon(b, g, ir.x, ir.y);
 221: 
 222:         if (text != null && ! text.equals(""))
 223:           {
 224:             Object htmlRenderer = b.getClientProperty(BasicHTML.propertyKey);
 225:             if (htmlRenderer == null)
 226:               {
 227:                 if (b.isEnabled())
 228:                   paintEnabledText(b, g, text, tr.x, tr.y + fm.getAscent());
 229:                 else
 230:                   paintDisabledText(b, g, text, tr.x, tr.y + fm.getAscent());
 231:               }
 232:             else
 233:               {
 234:                 ((View) htmlRenderer).paint(g, tr);
 235:               }
 236:           }
 237:       }
 238:   }
 239: 
 240:   /**
 241:    * This method is simply calls SwingUtilities's layoutCompoundLabel.
 242:    *
 243:    * @param label The label to lay out.
 244:    * @param fontMetrics The FontMetrics for the font used.
 245:    * @param text The text to paint.
 246:    * @param icon The icon to draw.
 247:    * @param viewR The entire viewable rectangle.
 248:    * @param iconR The icon bounds rectangle.
 249:    * @param textR The text bounds rectangle.
 250:    *
 251:    * @return A possibly clipped version of the text.
 252:    */
 253:   protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text,
 254:       Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR)
 255:   {
 256:     return SwingUtilities.layoutCompoundLabel(label, fontMetrics, text, icon,
 257:         label.getVerticalAlignment(), label.getHorizontalAlignment(), label
 258:             .getVerticalTextPosition(), label.getHorizontalTextPosition(),
 259:         viewR, iconR, textR, label.getIconTextGap());
 260:   }
 261: 
 262:   /**
 263:    * Paints the text if the label is disabled. By default, this paints the
 264:    * clipped text returned by layoutCompoundLabel using the
 265:    * background.brighter() color. It also paints the same text using the
 266:    * background.darker() color one pixel to the right and one pixel down.
 267:    *
 268:    * @param l The {@link JLabel} being painted.
 269:    * @param g The {@link Graphics} object to paint with.
 270:    * @param s The String to paint.
 271:    * @param textX The x coordinate of the start of the baseline.
 272:    * @param textY The y coordinate of the start of the baseline.
 273:    */
 274:   protected void paintDisabledText(JLabel l, Graphics g, String s, int textX,
 275:       int textY)
 276:   {
 277:     g.setColor(l.getBackground().brighter());
 278: 
 279:     int mnemIndex = l.getDisplayedMnemonicIndex();
 280: 
 281:     if (mnemIndex != -1)
 282:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
 283:           textY);
 284:     else
 285:       g.drawString(s, textX, textY);
 286: 
 287:     g.setColor(l.getBackground().darker());
 288:     if (mnemIndex != -1)
 289:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX + 1,
 290:           textY + 1);
 291:     else
 292:       g.drawString(s, textX + 1, textY + 1);
 293:   }
 294: 
 295:   /**
 296:    * Paints the text if the label is enabled. The text is painted using the
 297:    * foreground color.
 298:    *
 299:    * @param l The {@link JLabel} being painted.
 300:    * @param g The {@link Graphics} object to paint with.
 301:    * @param s The String to paint.
 302:    * @param textX The x coordinate of the start of the baseline.
 303:    * @param textY The y coordinate of the start of the baseline.
 304:    */
 305:   protected void paintEnabledText(JLabel l, Graphics g, String s, int textX,
 306:                                   int textY)
 307:   {
 308:     g.setColor(l.getForeground());
 309: 
 310:     int mnemIndex = l.getDisplayedMnemonicIndex();
 311: 
 312:     if (mnemIndex != -1)
 313:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
 314:           textY);
 315:     else
 316:       g.drawString(s, textX, textY);
 317:   }
 318: 
 319:   /**
 320:    * This method installs the UI for the given {@link JComponent}.  This
 321:    * method will install the component, defaults, listeners,  and keyboard
 322:    * actions.
 323:    *
 324:    * @param c The {@link JComponent} that this UI is being installed on.
 325:    */
 326:   public void installUI(JComponent c)
 327:   {
 328:     super.installUI(c);
 329:     if (c instanceof JLabel)
 330:     {
 331:       JLabel l = (JLabel) c;
 332: 
 333:       installComponents(l);
 334:       installDefaults(l);
 335:       installListeners(l);
 336:       installKeyboardActions(l);
 337:     }
 338:   }
 339: 
 340:   /**
 341:    * This method uninstalls the UI for the given {@link JComponent}. This
 342:    * method will uninstall the component, defaults, listeners,  and keyboard
 343:    * actions.
 344:    *
 345:    * @param c The {@link JComponent} that this UI is being installed on.
 346:    */
 347:   public void uninstallUI(JComponent c)
 348:   {
 349:     super.uninstallUI(c);
 350:     if (c instanceof JLabel)
 351:     {
 352:       JLabel l = (JLabel) c;
 353: 
 354:       uninstallKeyboardActions(l);
 355:       uninstallListeners(l);
 356:       uninstallDefaults(l);
 357:       uninstallComponents(l);
 358:     }
 359:   }
 360: 
 361:   /**
 362:    * This method installs the components for this {@link JLabel}.
 363:    *
 364:    * @param c The {@link JLabel} to install components for.
 365:    */
 366:   protected void installComponents(JLabel c)
 367:   {
 368:     BasicHTML.updateRenderer(c, c.getText());
 369:   }
 370: 
 371:   /**
 372:    * This method uninstalls the components for this {@link JLabel}.
 373:    *
 374:    * @param c The {@link JLabel} to uninstall components for.
 375:    */
 376:   protected void uninstallComponents(JLabel c)
 377:   {
 378:     c.putClientProperty(BasicHTML.propertyKey, null);
 379:     c.putClientProperty(BasicHTML.documentBaseKey, null);
 380:   }
 381: 
 382:   /**
 383:    * This method installs the defaults that are defined in  the Basic look and
 384:    * feel for this {@link JLabel}.
 385:    *
 386:    * @param c The {@link JLabel} to install defaults for.
 387:    */
 388:   protected void installDefaults(JLabel c)
 389:   {
 390:     LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground",
 391:                                      "Label.font");
 392:     //XXX: There are properties we don't use called disabledForeground
 393:     //and disabledShadow.
 394:   }
 395: 
 396:   /**
 397:    * This method uninstalls the defaults that are defined in the Basic look
 398:    * and feel for this {@link JLabel}.
 399:    *
 400:    * @param c The {@link JLabel} to uninstall defaults for.
 401:    */
 402:   protected void uninstallDefaults(JLabel c)
 403:   {
 404:     c.setForeground(null);
 405:     c.setBackground(null);
 406:     c.setFont(null);
 407:   }
 408: 
 409:   /**
 410:    * Installs the keyboard actions for the given {@link JLabel}.
 411:    *
 412:    * @param l The {@link JLabel} to install keyboard actions for.
 413:    */
 414:   protected void installKeyboardActions(JLabel l)
 415:   {
 416:     Component c = l.getLabelFor();
 417:     if (c != null)
 418:       {
 419:         int mnemonic = l.getDisplayedMnemonic();
 420:         if (mnemonic > 0)
 421:           {
 422:             // add a keystroke for the given mnemonic mapping to 'press';
 423:             InputMap keyMap = new InputMap();
 424:             keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.VK_ALT),
 425:                 "press");
 426:             SwingUtilities.replaceUIInputMap(l,
 427:                 JComponent.WHEN_IN_FOCUSED_WINDOW, keyMap);
 428: 
 429:             // add an action to focus the component when 'press' happens
 430:             ActionMap map = new ActionMap();
 431:             map.put("press", new AbstractAction() {
 432:               public void actionPerformed(ActionEvent event)
 433:               {
 434:                 JLabel label = (JLabel) event.getSource();
 435:                 Component c = label.getLabelFor();
 436:                 if (c != null)
 437:                   c.requestFocus();
 438:               }
 439:             });
 440:             SwingUtilities.replaceUIActionMap(l, map);
 441:           }
 442:       }
 443:   }
 444: 
 445:   /**
 446:    * This method uninstalls the keyboard actions for the given {@link JLabel}.
 447:    *
 448:    * @param l The {@link JLabel} to uninstall keyboard actions for.
 449:    */
 450:   protected void uninstallKeyboardActions(JLabel l)
 451:   {
 452:     SwingUtilities.replaceUIActionMap(l, null);
 453:     SwingUtilities.replaceUIInputMap(l, JComponent.WHEN_IN_FOCUSED_WINDOW,
 454:                                      null);
 455:   }
 456: 
 457:   /**
 458:    * This method installs the listeners for the  given {@link JLabel}. The UI
 459:    * delegate only listens to  the label.
 460:    *
 461:    * @param c The {@link JLabel} to install listeners for.
 462:    */
 463:   protected void installListeners(JLabel c)
 464:   {
 465:     c.addPropertyChangeListener(this);
 466:   }
 467: 
 468:   /**
 469:    * This method uninstalls the listeners for the given {@link JLabel}. The UI
 470:    * delegate only listens to the label.
 471:    *
 472:    * @param c The {@link JLabel} to uninstall listeners for.
 473:    */
 474:   protected void uninstallListeners(JLabel c)
 475:   {
 476:     c.removePropertyChangeListener(this);
 477:   }
 478: 
 479:   /**
 480:    * This method is called whenever any JLabel's that use this UI has one of
 481:    * their properties change.
 482:    *
 483:    * @param e The {@link PropertyChangeEvent} that describes the change.
 484:    */
 485:   public void propertyChange(PropertyChangeEvent e)
 486:   {
 487:     if (e.getPropertyName().equals("text"))
 488:       {
 489:         String text = (String) e.getNewValue();
 490:         JLabel l = (JLabel) e.getSource();
 491:         BasicHTML.updateRenderer(l, text);
 492:       }
 493:     else if (e.getPropertyName().equals("displayedMnemonic"))
 494:       {
 495:         // update the key to action mapping
 496:         JLabel label = (JLabel) e.getSource();
 497:         if (label.getLabelFor() != null)
 498:           {
 499:             int oldMnemonic = ((Integer) e.getOldValue()).intValue();
 500:             int newMnemonic = ((Integer) e.getNewValue()).intValue();
 501:             InputMap keyMap = label.getInputMap(
 502:                 JComponent.WHEN_IN_FOCUSED_WINDOW);
 503:             keyMap.put(KeyStroke.getKeyStroke(oldMnemonic,
 504:                 KeyEvent.ALT_DOWN_MASK), null);
 505:             keyMap.put(KeyStroke.getKeyStroke(newMnemonic,
 506:                 KeyEvent.ALT_DOWN_MASK), "press");
 507:           }
 508:       }
 509:     else if (e.getPropertyName().equals("labelFor"))
 510:       {
 511:         JLabel label = (JLabel) e.getSource();
 512:         InputMap keyMap = label.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 513:         int mnemonic = label.getDisplayedMnemonic();
 514:         if (mnemonic > 0)
 515:           keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.ALT_DOWN_MASK),
 516:               "press");
 517:       }
 518:   }
 519: 
 520:   /**
 521:    * Fetches a font metrics object for the specified label. This first
 522:    * tries to get it from the label object itself by calling
 523:    * {@link Component#getFontMetrics(Font)}, and if that does not work
 524:    * (for instance, when we are in the initialization and have no parent yet),
 525:    * it asks the Toolkit for a font metrics object.
 526:    *
 527:    * @param l the label
 528:    *
 529:    * @return a suitable font metrics object
 530:    */
 531:   private FontMetrics getFontMetrics(JLabel l)
 532:   {
 533:     Font font = l.getFont();
 534:     FontMetrics fm = l.getFontMetrics(font);
 535:     if (fm == null)
 536:       {
 537:         Toolkit tk = Toolkit.getDefaultToolkit();
 538:         fm = tk.getFontMetrics(font);
 539:       }
 540:     return fm;
 541:   }
 542: }