Source for javax.swing.plaf.basic.BasicMenuItemUI

   1: /* BasicMenuItemUI.java --
   2:    Copyright (C) 2002, 2004, 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.basic;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Dimension;
  44: import java.awt.Font;
  45: import java.awt.FontMetrics;
  46: import java.awt.Graphics;
  47: import java.awt.Insets;
  48: import java.awt.Rectangle;
  49: import java.awt.event.KeyEvent;
  50: import java.awt.event.MouseEvent;
  51: import java.beans.PropertyChangeEvent;
  52: import java.beans.PropertyChangeListener;
  53: import java.util.ArrayList;
  54: 
  55: import javax.swing.Icon;
  56: import javax.swing.JComponent;
  57: import javax.swing.JMenu;
  58: import javax.swing.JMenuItem;
  59: import javax.swing.JPopupMenu;
  60: import javax.swing.KeyStroke;
  61: import javax.swing.MenuElement;
  62: import javax.swing.MenuSelectionManager;
  63: import javax.swing.SwingConstants;
  64: import javax.swing.SwingUtilities;
  65: import javax.swing.UIDefaults;
  66: import javax.swing.UIManager;
  67: import javax.swing.event.MenuDragMouseEvent;
  68: import javax.swing.event.MenuDragMouseListener;
  69: import javax.swing.event.MenuKeyEvent;
  70: import javax.swing.event.MenuKeyListener;
  71: import javax.swing.event.MouseInputListener;
  72: import javax.swing.plaf.ComponentUI;
  73: import javax.swing.plaf.MenuItemUI;
  74: 
  75: /**
  76:  * UI Delegate for JMenuItem.
  77:  */
  78: public class BasicMenuItemUI extends MenuItemUI
  79: {
  80:   /**
  81:    * Font to be used when displaying menu item's accelerator.
  82:    */
  83:   protected Font acceleratorFont;
  84: 
  85:   /**
  86:    * Color to be used when displaying menu item's accelerator.
  87:    */
  88:   protected Color acceleratorForeground;
  89: 
  90:   /**
  91:    * Color to be used when displaying menu item's accelerator when menu item is
  92:    * selected.
  93:    */
  94:   protected Color acceleratorSelectionForeground;
  95: 
  96:   /**
  97:    * Icon that is displayed after the text to indicated that this menu contains
  98:    * submenu.
  99:    */
 100:   protected Icon arrowIcon;
 101: 
 102:   /**
 103:    * Icon that is displayed before the text. This icon is only used in
 104:    * JCheckBoxMenuItem or JRadioBoxMenuItem.
 105:    */
 106:   protected Icon checkIcon;
 107: 
 108:   /**
 109:    * Number of spaces between icon and text.
 110:    */
 111:   protected int defaultTextIconGap = 4;
 112: 
 113:   /**
 114:    * Color of the text when menu item is disabled
 115:    */
 116:   protected Color disabledForeground;
 117: 
 118:   /**
 119:    * The menu Drag mouse listener listening to the menu item.
 120:    */
 121:   protected MenuDragMouseListener menuDragMouseListener;
 122: 
 123:   /**
 124:    * The menu item itself
 125:    */
 126:   protected JMenuItem menuItem;
 127: 
 128:   /**
 129:    * Menu Key listener listening to the menu item.
 130:    */
 131:   protected MenuKeyListener menuKeyListener;
 132: 
 133:   /**
 134:    * mouse input listener listening to menu item.
 135:    */
 136:   protected MouseInputListener mouseInputListener;
 137: 
 138:   /**
 139:    * Indicates if border should be painted
 140:    */
 141:   protected boolean oldBorderPainted;
 142: 
 143:   /**
 144:    * Color of text that is used when menu item is selected
 145:    */
 146:   protected Color selectionBackground;
 147: 
 148:   /**
 149:    * Color of the text that is used when menu item is selected.
 150:    */
 151:   protected Color selectionForeground;
 152: 
 153:   /**
 154:    * String that separates description of the modifiers and the key
 155:    */
 156:   private String acceleratorDelimiter;
 157: 
 158:   /**
 159:    * PropertyChangeListener to listen for property changes in the menu item
 160:    */
 161:   private PropertyChangeListener propertyChangeListener;
 162: 
 163:   /**
 164:    * Number of spaces between accelerator and menu item's label.
 165:    */
 166:   private int defaultAcceleratorLabelGap = 4;
 167: 
 168:   /**
 169:    * Creates a new BasicMenuItemUI object.
 170:    */
 171:   public BasicMenuItemUI()
 172:   {
 173:     mouseInputListener = createMouseInputListener(menuItem);
 174:     menuDragMouseListener = createMenuDragMouseListener(menuItem);
 175:     menuKeyListener = createMenuKeyListener(menuItem);
 176:     propertyChangeListener = new PropertyChangeHandler();
 177:   }
 178: 
 179:   /**
 180:    * Create MenuDragMouseListener to listen for mouse dragged events.
 181:    *
 182:    * @param c menu item to listen to
 183:    *
 184:    * @return The MenuDragMouseListener
 185:    */
 186:   protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
 187:   {
 188:     return new MenuDragMouseHandler();
 189:   }
 190: 
 191:   /**
 192:    * Creates MenuKeyListener to listen to key events occuring when menu item
 193:    * is visible on the screen.
 194:    *
 195:    * @param c menu item to listen to
 196:    *
 197:    * @return The MenuKeyListener
 198:    */
 199:   protected MenuKeyListener createMenuKeyListener(JComponent c)
 200:   {
 201:     return new MenuKeyHandler();
 202:   }
 203: 
 204:   /**
 205:    * Handles mouse input events occuring for this menu item
 206:    *
 207:    * @param c menu item to listen to
 208:    *
 209:    * @return The MouseInputListener
 210:    */
 211:   protected MouseInputListener createMouseInputListener(JComponent c)
 212:   {
 213:     return new MouseInputHandler();
 214:   }
 215: 
 216:   /**
 217:    * Factory method to create a BasicMenuItemUI for the given {@link
 218:    * JComponent}, which should be a {@link JMenuItem}.
 219:    *
 220:    * @param c The {@link JComponent} a UI is being created for.
 221:    *
 222:    * @return A BasicMenuItemUI for the {@link JComponent}.
 223:    */
 224:   public static ComponentUI createUI(JComponent c)
 225:   {
 226:     return new BasicMenuItemUI();
 227:   }
 228: 
 229:   /**
 230:    * Programatically clicks menu item.
 231:    *
 232:    * @param msm MenuSelectionManager for the menu hierarchy
 233:    */
 234:   protected void doClick(MenuSelectionManager msm)
 235:   {
 236:     menuItem.doClick();
 237:     msm.clearSelectedPath();
 238:   }
 239: 
 240:   /**
 241:    * Returns maximum size for the specified menu item
 242:    *
 243:    * @param c component for which to get maximum size
 244:    *
 245:    * @return Maximum size for the specified menu item.
 246:    */
 247:   public Dimension getMaximumSize(JComponent c)
 248:   {
 249:     return null;
 250:   }
 251: 
 252:   /**
 253:    * Returns minimum size for the specified menu item
 254:    *
 255:    * @param c component for which to get minimum size
 256:    *
 257:    * @return Minimum size for the specified menu item.
 258:    */
 259:   public Dimension getMinimumSize(JComponent c)
 260:   {
 261:     return null;
 262:   }
 263: 
 264:   /**
 265:    * Returns path to this menu item.
 266:    *
 267:    * @return $MenuElement[]$ Returns array of menu elements
 268:    * that constitute a path to this menu item.
 269:    */
 270:   public MenuElement[] getPath()
 271:   {
 272:     ArrayList path = new ArrayList();
 273: 
 274:     // Path to menu should also include its popup menu.
 275:     if (menuItem instanceof JMenu)
 276:       path.add(((JMenu) menuItem).getPopupMenu());
 277: 
 278:     Component c = menuItem;
 279:     while (c instanceof MenuElement)
 280:       {
 281:     path.add(0, (MenuElement) c);
 282: 
 283:     if (c instanceof JPopupMenu)
 284:       c = ((JPopupMenu) c).getInvoker();
 285:     else
 286:       c = c.getParent();
 287:       }
 288: 
 289:     MenuElement[] pathArray = new MenuElement[path.size()];
 290:     path.toArray(pathArray);
 291:     return pathArray;
 292:   }
 293: 
 294:   /**
 295:    * Returns preferred size for the given menu item.
 296:    *
 297:    * @param c menu item for which to get preferred size
 298:    * @param checkIcon check icon displayed in the given menu item
 299:    * @param arrowIcon arrow icon displayed in the given menu item
 300:    * @param defaultTextIconGap space between icon and text in the given menuItem
 301:    *
 302:    * @return $Dimension$ preferred size for the given menu item
 303:    */
 304:   protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
 305:                                                Icon arrowIcon,
 306:                                                int defaultTextIconGap)
 307:   {
 308:     JMenuItem m = (JMenuItem) c;
 309:     Dimension d = BasicGraphicsUtils.getPreferredButtonSize(m,
 310:                                                             defaultTextIconGap);
 311: 
 312:     // if menu item has accelerator then take accelerator's size into account
 313:     // when calculating preferred size.
 314:     KeyStroke accelerator = m.getAccelerator();
 315:     Rectangle rect;
 316: 
 317:     if (accelerator != null)
 318:       {
 319:     rect = getAcceleratorRect(accelerator,
 320:                               m.getToolkit().getFontMetrics(acceleratorFont));
 321: 
 322:     // add width of accelerator's text
 323:     d.width = d.width + rect.width + defaultAcceleratorLabelGap;
 324: 
 325:     // adjust the heigth of the preferred size if necessary
 326:     if (d.height < rect.height)
 327:       d.height = rect.height;
 328:       }
 329: 
 330:     if (checkIcon != null)
 331:       {
 332:     d.width = d.width + checkIcon.getIconWidth() + defaultTextIconGap;
 333: 
 334:     if (checkIcon.getIconHeight() > d.height)
 335:       d.height = checkIcon.getIconHeight();
 336:       }
 337: 
 338:     if (arrowIcon != null && (c instanceof JMenu))
 339:       {
 340:     d.width = d.width + arrowIcon.getIconWidth() + defaultTextIconGap;
 341: 
 342:     if (arrowIcon.getIconHeight() > d.height)
 343:       d.height = arrowIcon.getIconHeight();
 344:       }
 345: 
 346:     return d;
 347:   }
 348: 
 349:   /**
 350:    * Returns preferred size of the given component
 351:    *
 352:    * @param c component for which to return preferred size
 353:    *
 354:    * @return $Dimension$ preferred size for the given component
 355:    */
 356:   public Dimension getPreferredSize(JComponent c)
 357:   {
 358:     return getPreferredMenuItemSize(c, checkIcon, arrowIcon, 
 359:         defaultTextIconGap);
 360:   }
 361: 
 362:   /**
 363:    * Returns the prefix for entries in the {@link UIDefaults} table.
 364:    *
 365:    * @return "MenuItem"
 366:    */
 367:   protected String getPropertyPrefix()
 368:   {
 369:     return "MenuItem";
 370:   }
 371: 
 372:   /**
 373:    * This method installs the components for this {@link JMenuItem}.
 374:    *
 375:    * @param menuItem The {@link JMenuItem} to install components for.
 376:    */
 377:   protected void installComponents(JMenuItem menuItem)
 378:   {
 379:     // FIXME: Need to implement
 380:   }
 381: 
 382:   /**
 383:    * This method installs the defaults that are defined in  the Basic look and
 384:    * feel for this {@link JMenuItem}.
 385:    */
 386:   protected void installDefaults()
 387:   {
 388:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 389: 
 390:     menuItem.setBackground(defaults.getColor("MenuItem.background"));
 391:     menuItem.setBorder(defaults.getBorder("MenuItem.border"));
 392:     menuItem.setFont(defaults.getFont("MenuItem.font"));
 393:     menuItem.setForeground(defaults.getColor("MenuItem.foreground"));
 394:     menuItem.setMargin(defaults.getInsets("MenuItem.margin"));
 395:     menuItem.setOpaque(true);
 396:     acceleratorFont = defaults.getFont("MenuItem.acceleratorFont");
 397:     acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground");
 398:     acceleratorSelectionForeground = defaults.getColor("MenuItem.acceleratorSelectionForeground");
 399:     selectionBackground = defaults.getColor("MenuItem.selectionBackground");
 400:     selectionForeground = defaults.getColor("MenuItem.selectionForeground");
 401:     acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter");
 402: 
 403:     menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
 404:     menuItem.setHorizontalAlignment(SwingConstants.LEADING);
 405:   }
 406: 
 407:   /**
 408:    * This method installs the keyboard actions for this {@link JMenuItem}.
 409:    */
 410:   protected void installKeyboardActions()
 411:   {
 412:     // FIXME: Need to implement
 413:   }
 414: 
 415:   /**
 416:    * This method installs the listeners for the {@link JMenuItem}.
 417:    */
 418:   protected void installListeners()
 419:   {
 420:     menuItem.addMouseListener(mouseInputListener);
 421:     menuItem.addMouseMotionListener(mouseInputListener);
 422:     menuItem.addMenuDragMouseListener(menuDragMouseListener);
 423:     menuItem.addMenuKeyListener(menuKeyListener);
 424:     menuItem.addPropertyChangeListener(propertyChangeListener);
 425:   }
 426: 
 427:   /**
 428:    * Installs and initializes all fields for this UI delegate. Any properties
 429:    * of the UI that need to be initialized and/or set to defaults will be
 430:    * done now. It will also install any listeners necessary.
 431:    *
 432:    * @param c The {@link JComponent} that is having this UI installed.
 433:    */
 434:   public void installUI(JComponent c)
 435:   {
 436:     super.installUI(c);
 437:     menuItem = (JMenuItem) c;
 438:     installDefaults();
 439:     installComponents(menuItem);
 440:     installListeners();
 441:   }
 442: 
 443:   /**
 444:    * Paints given menu item using specified graphics context
 445:    *
 446:    * @param g The graphics context used to paint this menu item
 447:    * @param c Menu Item to paint
 448:    */
 449:   public void paint(Graphics g, JComponent c)
 450:   {
 451:     paintMenuItem(g, c, checkIcon, arrowIcon, c.getBackground(),
 452:                   c.getForeground(), defaultTextIconGap);
 453:   }
 454: 
 455:   /**
 456:    * Paints background of the menu item
 457:    *
 458:    * @param g The graphics context used to paint this menu item
 459:    * @param menuItem menu item to paint
 460:    * @param bgColor Background color to use when painting menu item
 461:    */
 462:   protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
 463:   {
 464:     Dimension size = getPreferredSize(menuItem);
 465:     Color foreground = g.getColor();
 466:     g.setColor(bgColor);
 467:     g.drawRect(0, 0, size.width, size.height);
 468:     g.setColor(foreground);
 469:   }
 470: 
 471:   /**
 472:    * Paints specified menu item
 473:    *
 474:    * @param g The graphics context used to paint this menu item
 475:    * @param c menu item to paint
 476:    * @param checkIcon check icon to use when painting menu item
 477:    * @param arrowIcon arrow icon to use when painting menu item
 478:    * @param background Background color of the menu item
 479:    * @param foreground Foreground color of the menu item
 480:    * @param defaultTextIconGap space to use between icon and
 481:    *  text when painting menu item
 482:    */
 483:   protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
 484:                                Icon arrowIcon, Color background,
 485:                                Color foreground, int defaultTextIconGap)
 486:   {
 487:     JMenuItem m = (JMenuItem) c;
 488:     Rectangle tr = new Rectangle(); // text rectangle
 489:     Rectangle ir = new Rectangle(); // icon rectangle
 490:     Rectangle vr = new Rectangle(); // view rectangle
 491:     Rectangle br = new Rectangle(); // border rectangle
 492:     Rectangle ar = new Rectangle(); // accelerator rectangle
 493:     Rectangle cr = new Rectangle(); // checkIcon rectangle
 494: 
 495:     int vertAlign = m.getVerticalAlignment();
 496:     int horAlign = m.getHorizontalAlignment();
 497:     int vertTextPos = m.getVerticalTextPosition();
 498:     int horTextPos = m.getHorizontalTextPosition();
 499: 
 500:     Font f = m.getFont();
 501:     g.setFont(f);
 502:     FontMetrics fm = g.getFontMetrics(f);
 503:     SwingUtilities.calculateInnerArea(m, br);
 504:     SwingUtilities.calculateInsetArea(br, m.getInsets(), vr);
 505:     paintBackground(g, m, m.getBackground());
 506: 
 507:     /* MenuItems insets are equal to menuItems margin, space between text and
 508:        menuItems border. We need to paint insets region as well. */
 509:     Insets insets = m.getInsets();
 510:     br.x -= insets.left;
 511:     br.y -= insets.top;
 512:     br.width += insets.right + insets.left;
 513:     br.height += insets.top + insets.bottom;
 514: 
 515:     // Menu item is considered to be highlighted when it is selected.
 516:     // But we don't want to paint the background of JCheckBoxMenuItems
 517:     if ((m.isSelected() && checkIcon == null) || m.getModel().isArmed() && 
 518:         (m.getParent() instanceof MenuElement)) 
 519:       {
 520:     if (m.isContentAreaFilled())
 521:       {
 522:         g.setColor(selectionBackground);
 523:         g.fillRect(br.x, br.y, br.width, br.height);
 524:       }
 525:       }
 526:     else
 527:       {
 528:     if (m.isContentAreaFilled())
 529:       {
 530:         g.setColor(m.getBackground());
 531:         g.fillRect(br.x, br.y, br.width, br.height);
 532:       }
 533:       }
 534: 
 535:     // If this menu item is a JCheckBoxMenuItem then paint check icon
 536:     if (checkIcon != null)
 537:       {
 538:     SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign,
 539:                                        horAlign, vertTextPos, horTextPos,
 540:                                        vr, cr, tr, defaultTextIconGap);
 541:         checkIcon.paintIcon(m, g, cr.x, cr.y);
 542:     // We need to calculate position of the menu text and position of
 543:     // user menu icon if there exists one relative to the check icon.
 544:     // So we need to adjust view rectangle s.t. its starting point is at
 545:     // checkIcon.width + defaultTextIconGap. 
 546:     vr.x = cr.x + cr.width + defaultTextIconGap;
 547:       }
 548: 
 549:     // if this is a submenu, then paint arrow icon to indicate it.
 550:     if (arrowIcon != null && (c instanceof JMenu))
 551:       {
 552:     if (! ((JMenu) c).isTopLevelMenu())
 553:       {
 554:         int width = arrowIcon.getIconWidth();
 555:         int height = arrowIcon.getIconHeight();
 556: 
 557:         arrowIcon.paintIcon(m, g, vr.width - width + defaultTextIconGap,
 558:                             vr.y + 2);
 559:       }
 560:       }
 561: 
 562:     // paint text and user menu icon if it exists         
 563:     Icon i = m.getIcon();
 564:     SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i,
 565:                                        vertAlign, horAlign, vertTextPos,
 566:                                        horTextPos, vr, ir, tr,
 567:                                        defaultTextIconGap);
 568:     if (i != null)
 569:       i.paintIcon(c, g, ir.x, ir.y);
 570:     paintText(g, m, tr, m.getText());
 571: 
 572:     // paint accelerator    
 573:     String acceleratorText = "";
 574: 
 575:     if (m.getAccelerator() != null)
 576:       {
 577:     acceleratorText = getAcceleratorText(m.getAccelerator());
 578:     fm = g.getFontMetrics(acceleratorFont);
 579:     ar.width = fm.stringWidth(acceleratorText);
 580:     ar.x = br.width - ar.width;
 581:     vr.x = br.width - ar.width;
 582: 
 583:     SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null,
 584:                                        vertAlign, horAlign, vertTextPos,
 585:                                        horTextPos, vr, ir, ar,
 586:                                        defaultTextIconGap);
 587: 
 588:     paintAccelerator(g, m, ar, acceleratorText);
 589:       }
 590:   }
 591: 
 592:   /**
 593:    * Paints label for the given menu item
 594:    *
 595:    * @param g The graphics context used to paint this menu item
 596:    * @param menuItem menu item for which to draw its label
 597:    * @param textRect rectangle specifiying position of the text relative to
 598:    * the given menu item
 599:    * @param text label of the menu item
 600:    */
 601:   protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
 602:                            String text)
 603:   {
 604:     Font f = menuItem.getFont();
 605:     g.setFont(f);
 606:     FontMetrics fm = g.getFontMetrics(f);
 607: 
 608:     if (text != null && ! text.equals(""))
 609:       {
 610:     if (menuItem.isEnabled())
 611:           {
 612:             // Menu item is considered to be highlighted when it is selected.
 613:             // But not if it's a JCheckBoxMenuItem
 614:             if ((menuItem.isSelected() && checkIcon == null) || menuItem.getModel().isArmed() && 
 615:                 (menuItem.getParent() instanceof MenuElement)) 
 616:               g.setColor(selectionForeground);
 617:             else
 618:               g.setColor(menuItem.getForeground());
 619:           }
 620:     else
 621:       // FIXME: should fix this to use 'disabledForeground', but its
 622:       // default value in BasicLookAndFeel is null.      
 623:           
 624:           // FIXME: should there be different foreground colours for selected
 625:           // or deselected, when disabled?
 626:           g.setColor(Color.gray);
 627: 
 628:     int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
 629: 
 630:     if (mnemonicIndex != -1)
 631:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex,
 632:                                                    textRect.x,
 633:                                                    textRect.y
 634:                                                    + fm.getAscent());
 635:     else
 636:       BasicGraphicsUtils.drawString(g, text, 0, textRect.x,
 637:                                     textRect.y + fm.getAscent());
 638:       }
 639:   }
 640: 
 641:   /**
 642:    * This method uninstalls the components for this {@link JMenuItem}.
 643:    *
 644:    * @param menuItem The {@link JMenuItem} to uninstall components for.
 645:    */
 646:   protected void uninstallComponents(JMenuItem menuItem)
 647:   {
 648:     // FIXME: need to implement
 649:   }
 650: 
 651:   /**
 652:    * This method uninstalls the defaults and sets any objects created during
 653:    * install to null
 654:    */
 655:   protected void uninstallDefaults()
 656:   {
 657:     menuItem.setForeground(null);
 658:     menuItem.setBackground(null);
 659:     menuItem.setBorder(null);
 660:     menuItem.setMargin(null);
 661:     menuItem.setBackground(null);
 662:     menuItem.setBorder(null);
 663:     menuItem.setFont(null);
 664:     menuItem.setForeground(null);
 665:     menuItem.setMargin(null);
 666:     acceleratorFont = null;
 667:     acceleratorForeground = null;
 668:     acceleratorSelectionForeground = null;
 669:     arrowIcon = null;
 670:     selectionBackground = null;
 671:     selectionForeground = null;
 672:     acceleratorDelimiter = null;
 673:   }
 674: 
 675:   /**
 676:    * Uninstalls any keyboard actions.
 677:    */
 678:   protected void uninstallKeyboardActions()
 679:   {
 680:     // FIXME: need to implement
 681:   }
 682: 
 683:   /**
 684:    * Unregisters all the listeners that this UI delegate was using.
 685:    */
 686:   protected void uninstallListeners()
 687:   {
 688:     menuItem.removeMouseListener(mouseInputListener);
 689:     menuItem.removeMenuDragMouseListener(menuDragMouseListener);
 690:     menuItem.removeMenuKeyListener(menuKeyListener);
 691:     menuItem.removePropertyChangeListener(propertyChangeListener);
 692:   }
 693: 
 694:   /**
 695:    * Performs the opposite of installUI. Any properties or resources that need
 696:    * to be cleaned up will be done now. It will also uninstall any listeners
 697:    * it has. In addition, any properties of this UI will be nulled.
 698:    *
 699:    * @param c The {@link JComponent} that is having this UI uninstalled.
 700:    */
 701:   public void uninstallUI(JComponent c)
 702:   {
 703:     uninstallListeners();
 704:     uninstallDefaults();
 705:     uninstallComponents(menuItem);
 706:     menuItem = null;
 707:   }
 708: 
 709:   /**
 710:    * This method calls paint.
 711:    *
 712:    * @param g The graphics context used to paint this menu item
 713:    * @param c The menu item to paint
 714:    */
 715:   public void update(Graphics g, JComponent c)
 716:   {
 717:     paint(g, c);
 718:   }
 719: 
 720:   /**
 721:    * Return text representation of the specified accelerator
 722:    *
 723:    * @param accelerator Accelerator for which to return string representation
 724:    *
 725:    * @return $String$ Text representation of the given accelerator
 726:    */
 727:   private String getAcceleratorText(KeyStroke accelerator)
 728:   {
 729:     // convert keystroke into string format
 730:     String modifiersText = "";
 731:     int modifiers = accelerator.getModifiers();
 732:     char keyChar = accelerator.getKeyChar();
 733:     int keyCode = accelerator.getKeyCode();
 734: 
 735:     if (modifiers != 0)
 736:       modifiersText = KeyEvent.getKeyModifiersText(modifiers)
 737:                       + acceleratorDelimiter;
 738: 
 739:     if (keyCode == KeyEvent.VK_UNDEFINED)
 740:       return modifiersText + keyChar;
 741:     else
 742:       return modifiersText + KeyEvent.getKeyText(keyCode);
 743:   }
 744: 
 745:   /**
 746:    * Calculates and return rectange in which accelerator should be displayed
 747:    *
 748:    * @param accelerator accelerator for which to return the display rectangle
 749:    * @param fm The font metrics used to measure the text
 750:    *
 751:    * @return $Rectangle$ reactangle which will be used to display accelerator
 752:    */
 753:   private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
 754:   {
 755:     int width = fm.stringWidth(getAcceleratorText(accelerator));
 756:     int height = fm.getHeight();
 757:     return new Rectangle(0, 0, width, height);
 758:   }
 759: 
 760:   /**
 761:    * Paints accelerator inside menu item
 762:    *
 763:    * @param g The graphics context used to paint the border
 764:    * @param menuItem Menu item for which to draw accelerator
 765:    * @param acceleratorRect rectangle representing position
 766:    * of the accelerator relative to the menu item
 767:    * @param acceleratorText accelerator's text
 768:    */
 769:   private void paintAccelerator(Graphics g, JMenuItem menuItem,
 770:                                 Rectangle acceleratorRect,
 771:                                 String acceleratorText)
 772:   {
 773:     g.setFont(acceleratorFont);
 774:     FontMetrics fm = g.getFontMetrics(acceleratorFont);
 775: 
 776:     if (menuItem.isEnabled())
 777:       g.setColor(acceleratorForeground);
 778:     else
 779:       // FIXME: should fix this to use 'disabledForeground', but its
 780:       // default value in BasicLookAndFeel is null.
 781:       g.setColor(Color.gray);
 782: 
 783:     BasicGraphicsUtils.drawString(g, acceleratorText, 0, acceleratorRect.x,
 784:                                   acceleratorRect.y + fm.getAscent());
 785:   }
 786: 
 787:   /**
 788:    * This class handles mouse events occuring inside the menu item.
 789:    * Most of the events are forwarded for processing to MenuSelectionManager
 790:    * of the current menu hierarchy.
 791:    *
 792:    */
 793:   protected class MouseInputHandler implements MouseInputListener
 794:   {
 795:     /**
 796:      * Creates a new MouseInputHandler object.
 797:      */
 798:     protected MouseInputHandler()
 799:     {
 800:     }
 801: 
 802:     /**
 803:      * This method is called when mouse is clicked on the menu item.
 804:      * It forwards this event to MenuSelectionManager.
 805:      *
 806:      * @param e A {@link MouseEvent}.
 807:      */
 808:     public void mouseClicked(MouseEvent e)
 809:     {
 810:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 811:       manager.processMouseEvent(e);
 812:     }
 813: 
 814:     /**
 815:      * This method is called when mouse is dragged inside the menu item.
 816:      * It forwards this event to MenuSelectionManager.
 817:      *
 818:      * @param e A {@link MouseEvent}.
 819:      */
 820:     public void mouseDragged(MouseEvent e)
 821:     {
 822:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 823:       manager.processMouseEvent(e);
 824:     }
 825: 
 826:     /**
 827:      * This method is called when mouse enters menu item.
 828:      * When this happens menu item is considered to be selected and selection path
 829:      * in MenuSelectionManager is set. This event is also forwarded to MenuSelection
 830:      * Manager for further processing.
 831:      *
 832:      * @param e A {@link MouseEvent}.
 833:      */
 834:     public void mouseEntered(MouseEvent e)
 835:     {
 836:       Component source = (Component) e.getSource();
 837:       if (source.getParent() instanceof MenuElement)
 838:         {
 839:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 840:       manager.setSelectedPath(getPath());
 841:       manager.processMouseEvent(e);
 842:         }
 843:     }
 844: 
 845:     /**
 846:      * This method is called when mouse exits menu item. The event is
 847:      * forwarded to MenuSelectionManager for processing.
 848:      *
 849:      * @param e A {@link MouseEvent}.
 850:      */
 851:     public void mouseExited(MouseEvent e)
 852:     {
 853:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 854:       manager.processMouseEvent(e);
 855:     }
 856: 
 857:     /**
 858:      * This method is called when mouse is inside the menu item.
 859:      * This event is forwarder to MenuSelectionManager for further processing.
 860:      *
 861:      * @param e A {@link MouseEvent}.
 862:      */
 863:     public void mouseMoved(MouseEvent e)
 864:     {
 865:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 866:       manager.processMouseEvent(e);
 867:     }
 868: 
 869:     /**
 870:      * This method is called when mouse is pressed. This event is forwarded to
 871:      * MenuSelectionManager for further processing.
 872:      *
 873:      * @param e A {@link MouseEvent}.
 874:      */
 875:     public void mousePressed(MouseEvent e)
 876:     {
 877:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 878:       manager.processMouseEvent(e);
 879:     }
 880: 
 881:     /**
 882:      * This method is called when mouse is released. If the mouse is released
 883:      * inside this menuItem, then this menu item is considered to be chosen and
 884:      * the menu hierarchy should be closed.
 885:      *
 886:      * @param e A {@link MouseEvent}.
 887:      */
 888:     public void mouseReleased(MouseEvent e)
 889:     {
 890:       Rectangle size = menuItem.getBounds();
 891:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 892:       if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0
 893:           && e.getY() < size.height)
 894:         {
 895:       manager.clearSelectedPath();
 896:       menuItem.doClick();
 897:         }
 898: 
 899:       else
 900:     manager.processMouseEvent(e);
 901:     }
 902:   }
 903: 
 904:   /**
 905:    * This class handles mouse dragged events.
 906:    */
 907:   protected class MenuDragMouseHandler implements MenuDragMouseListener
 908:   {
 909:     /**
 910:      * Tbis method is invoked when mouse is dragged over the menu item.
 911:      *
 912:      * @param e The MenuDragMouseEvent
 913:      */
 914:     public void menuDragMouseDragged(MenuDragMouseEvent e)
 915:     {
 916:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 917:       manager.setSelectedPath(e.getPath());
 918:     }
 919: 
 920:     /**
 921:      * Tbis method is invoked when mouse enters the menu item while it is
 922:      * being dragged.
 923:      *
 924:      * @param e The MenuDragMouseEvent
 925:      */
 926:     public void menuDragMouseEntered(MenuDragMouseEvent e)
 927:     {
 928:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 929:       manager.setSelectedPath(e.getPath());
 930:     }
 931: 
 932:     /**
 933:      * Tbis method is invoked when mouse exits the menu item while
 934:      * it is being dragged
 935:      *
 936:      * @param e The MenuDragMouseEvent
 937:      */
 938:     public void menuDragMouseExited(MenuDragMouseEvent e)
 939:     {
 940:     }
 941: 
 942:     /**
 943:      * Tbis method is invoked when mouse was dragged and released
 944:      * inside the menu item.
 945:      *
 946:      * @param e The MenuDragMouseEvent
 947:      */
 948:     public void menuDragMouseReleased(MenuDragMouseEvent e)
 949:     {
 950:       MenuElement[] path = e.getPath();
 951: 
 952:       if (path[path.length - 1] instanceof JMenuItem)
 953:     ((JMenuItem) path[path.length - 1]).doClick();
 954: 
 955:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 956:       manager.clearSelectedPath();
 957:     }
 958:   }
 959: 
 960:   /**
 961:    * This class handles key events occuring when menu item is visible on the
 962:    * screen.
 963:    */
 964:   protected class MenuKeyHandler implements MenuKeyListener
 965:   {
 966:     /**
 967:      * This method is invoked when key has been pressed
 968:      *
 969:      * @param e A {@link MenuKeyEvent}.
 970:      */
 971:     public void menuKeyPressed(MenuKeyEvent e)
 972:     {
 973:     }
 974: 
 975:     /**
 976:      * This method is invoked when key has been pressed
 977:      *
 978:      * @param e A {@link MenuKeyEvent}.
 979:      */
 980:     public void menuKeyReleased(MenuKeyEvent e)
 981:     {
 982:     }
 983: 
 984:     /**
 985:      * This method is invoked when key has been typed
 986:      * It handles the mnemonic key for the menu item.
 987:      *
 988:      * @param e A {@link MenuKeyEvent}.
 989:      */
 990:     public void menuKeyTyped(MenuKeyEvent e)
 991:     {
 992:     }
 993:   }
 994: 
 995:   /**
 996:    * Helper class that listens for changes to the properties of the {@link
 997:    * JMenuItem}.
 998:    */
 999:   protected class PropertyChangeHandler implements PropertyChangeListener
1000:   {
1001:     /**
1002:      * This method is called when one of the menu item's properties change.
1003:      *
1004:      * @param evt A {@link PropertyChangeEvent}.
1005:      */
1006:     public void propertyChange(PropertyChangeEvent evt)
1007:     {
1008:       menuItem.revalidate();
1009:       menuItem.repaint();
1010:     }
1011:   }
1012: }