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