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 chech 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, defaultTextIconGap);
 359:   }
 360: 
 361:   protected String getPropertyPrefix()
 362:   {
 363:     return null;
 364:   }
 365: 
 366:   /**
 367:    * This method installs the components for this {@link JMenuItem}.
 368:    *
 369:    * @param menuItem The {@link JMenuItem} to install components for.
 370:    */
 371:   protected void installComponents(JMenuItem menuItem)
 372:   {
 373:     // FIXME: Need to implement
 374:   }
 375: 
 376:   /**
 377:    * This method installs the defaults that are defined in  the Basic look and
 378:    * feel for this {@link JMenuItem}.
 379:    */
 380:   protected void installDefaults()
 381:   {
 382:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 383: 
 384:     menuItem.setBackground(defaults.getColor("MenuItem.background"));
 385:     menuItem.setBorder(defaults.getBorder("MenuItem.border"));
 386:     menuItem.setFont(defaults.getFont("MenuItem.font"));
 387:     menuItem.setForeground(defaults.getColor("MenuItem.foreground"));
 388:     menuItem.setMargin(defaults.getInsets("MenuItem.margin"));
 389:     menuItem.setOpaque(true);
 390:     acceleratorFont = defaults.getFont("MenuItem.acceleratorFont");
 391:     acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground");
 392:     acceleratorSelectionForeground = defaults.getColor("MenuItem.acceleratorSelectionForeground");
 393:     selectionBackground = defaults.getColor("MenuItem.selectionBackground");
 394:     selectionForeground = defaults.getColor("MenuItem.selectionForeground");
 395:     acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter");
 396: 
 397:     menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
 398:     menuItem.setHorizontalAlignment(SwingConstants.LEADING);
 399:   }
 400: 
 401:   /**
 402:    * This method installs the keyboard actions for this {@link JMenuItem}.
 403:    */
 404:   protected void installKeyboardActions()
 405:   {
 406:     // FIXME: Need to implement
 407:   }
 408: 
 409:   /**
 410:    * This method installs the listeners for the {@link JMenuItem}.
 411:    */
 412:   protected void installListeners()
 413:   {
 414:     menuItem.addMouseListener(mouseInputListener);
 415:     menuItem.addMouseMotionListener(mouseInputListener);
 416:     menuItem.addMenuDragMouseListener(menuDragMouseListener);
 417:     menuItem.addMenuKeyListener(menuKeyListener);
 418:     menuItem.addPropertyChangeListener(propertyChangeListener);
 419:   }
 420: 
 421:   /**
 422:    * Installs and initializes all fields for this UI delegate. Any properties
 423:    * of the UI that need to be initialized and/or set to defaults will be
 424:    * done now. It will also install any listeners necessary.
 425:    *
 426:    * @param c The {@link JComponent} that is having this UI installed.
 427:    */
 428:   public void installUI(JComponent c)
 429:   {
 430:     super.installUI(c);
 431:     menuItem = (JMenuItem) c;
 432:     installDefaults();
 433:     installComponents(menuItem);
 434:     installListeners();
 435:   }
 436: 
 437:   /**
 438:    * Paints given menu item using specified graphics context
 439:    *
 440:    * @param g The graphics context used to paint this menu item
 441:    * @param c Menu Item to paint
 442:    */
 443:   public void paint(Graphics g, JComponent c)
 444:   {
 445:     paintMenuItem(g, c, checkIcon, arrowIcon, c.getBackground(),
 446:                   c.getForeground(), defaultTextIconGap);
 447:   }
 448: 
 449:   /**
 450:    * Paints background of the menu item
 451:    *
 452:    * @param g The graphics context used to paint this menu item
 453:    * @param menuItem menu item to paint
 454:    * @param bgColor Background color to use when painting menu item
 455:    */
 456:   protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
 457:   {
 458:     Dimension size = getPreferredSize(menuItem);
 459:     Color foreground = g.getColor();
 460:     g.setColor(bgColor);
 461:     g.drawRect(0, 0, size.width, size.height);
 462:     g.setColor(foreground);
 463:   }
 464: 
 465:   /**
 466:    * Paints specified menu item
 467:    *
 468:    * @param g The graphics context used to paint this menu item
 469:    * @param c menu item to paint
 470:    * @param checkIcon check icon to use when painting menu item
 471:    * @param arrowIcon arrow icon to use when painting menu item
 472:    * @param background Background color of the menu item
 473:    * @param foreground Foreground color of the menu item
 474:    * @param defaultTextIconGap space to use between icon and
 475:    *  text when painting menu item
 476:    */
 477:   protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
 478:                                Icon arrowIcon, Color background,
 479:                                Color foreground, int defaultTextIconGap)
 480:   {
 481:     JMenuItem m = (JMenuItem) c;
 482:     Rectangle tr = new Rectangle(); // text rectangle
 483:     Rectangle ir = new Rectangle(); // icon rectangle
 484:     Rectangle vr = new Rectangle(); // view rectangle
 485:     Rectangle br = new Rectangle(); // border rectangle
 486:     Rectangle ar = new Rectangle(); // accelerator rectangle
 487:     Rectangle cr = new Rectangle(); // checkIcon rectangle
 488: 
 489:     int vertAlign = m.getVerticalAlignment();
 490:     int horAlign = m.getHorizontalAlignment();
 491:     int vertTextPos = m.getVerticalTextPosition();
 492:     int horTextPos = m.getHorizontalTextPosition();
 493: 
 494:     Font f = m.getFont();
 495:     g.setFont(f);
 496:     FontMetrics fm = g.getFontMetrics(f);
 497:     SwingUtilities.calculateInnerArea(m, br);
 498:     SwingUtilities.calculateInsetArea(br, m.getInsets(), vr);
 499:     paintBackground(g, m, m.getBackground());
 500: 
 501:     /* MenuItems insets are equal to menuItems margin, space between text and
 502:        menuItems border. We need to paint insets region as well. */
 503:     Insets insets = m.getInsets();
 504:     br.x -= insets.left;
 505:     br.y -= insets.top;
 506:     br.width += insets.right + insets.left;
 507:     br.height += insets.top + insets.bottom;
 508: 
 509:     // Menu item is considered to be highlighted when it is selected.
 510:     if (m.isSelected() || m.getModel().isArmed() && 
 511:         (m.getParent() instanceof MenuElement)) 
 512:       {
 513:     if (m.isContentAreaFilled())
 514:       {
 515:         g.setColor(selectionBackground);
 516:         g.fillRect(br.x, br.y, br.width, br.height);
 517:       }
 518:       }
 519:     else
 520:       {
 521:     if (m.isContentAreaFilled())
 522:       {
 523:         g.setColor(m.getBackground());
 524:         g.fillRect(br.x, br.y, br.width, br.height);
 525:       }
 526:       }
 527: 
 528:     // If this menu item is a JCheckBoxMenuItem then paint check icon
 529:     if (checkIcon != null)
 530:       {
 531:     SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign,
 532:                                        horAlign, vertTextPos, horTextPos,
 533:                                        vr, cr, tr, defaultTextIconGap);
 534:     checkIcon.paintIcon(m, g, cr.x, cr.y);
 535: 
 536:     // We need to calculate position of the menu text and position of
 537:     // user menu icon if there exists one relative to the check icon.
 538:     // So we need to adjust view rectangle s.t. its starting point is at
 539:     // checkIcon.width + defaultTextIconGap. 
 540:     vr.x = cr.x + cr.width + defaultTextIconGap;
 541:       }
 542: 
 543:     // if this is a submenu, then paint arrow icon to indicate it.
 544:     if (arrowIcon != null && (c instanceof JMenu))
 545:       {
 546:     if (! ((JMenu) c).isTopLevelMenu())
 547:       {
 548:         int width = arrowIcon.getIconWidth();
 549:         int height = arrowIcon.getIconHeight();
 550: 
 551:         arrowIcon.paintIcon(m, g, vr.width - width + defaultTextIconGap,
 552:                             vr.y + 2);
 553:       }
 554:       }
 555: 
 556:     // paint text and user menu icon if it exists         
 557:     Icon i = m.getIcon();
 558:     SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i,
 559:                                        vertAlign, horAlign, vertTextPos,
 560:                                        horTextPos, vr, ir, tr,
 561:                                        defaultTextIconGap);
 562:     if (i != null)
 563:       i.paintIcon(c, g, ir.x, ir.y);
 564: 
 565:     paintText(g, m, tr, m.getText());
 566: 
 567:     // paint accelerator    
 568:     String acceleratorText = "";
 569: 
 570:     if (m.getAccelerator() != null)
 571:       {
 572:     acceleratorText = getAcceleratorText(m.getAccelerator());
 573:     fm = g.getFontMetrics(acceleratorFont);
 574:     ar.width = fm.stringWidth(acceleratorText);
 575:     ar.x = br.width - ar.width;
 576:     vr.x = br.width - ar.width;
 577: 
 578:     SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null,
 579:                                        vertAlign, horAlign, vertTextPos,
 580:                                        horTextPos, vr, ir, ar,
 581:                                        defaultTextIconGap);
 582: 
 583:     paintAccelerator(g, m, ar, acceleratorText);
 584:       }
 585:   }
 586: 
 587:   /**
 588:    * Paints label for the given menu item
 589:    *
 590:    * @param g The graphics context used to paint this menu item
 591:    * @param menuItem menu item for which to draw its label
 592:    * @param textRect rectangle specifiying position of the text relative to
 593:    * the given menu item
 594:    * @param text label of the menu item
 595:    */
 596:   protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
 597:                            String text)
 598:   {
 599:     Font f = menuItem.getFont();
 600:     g.setFont(f);
 601:     FontMetrics fm = g.getFontMetrics(f);
 602: 
 603:     if (text != null && ! text.equals(""))
 604:       {
 605:     if (menuItem.isEnabled())
 606:           {
 607:             // Menu item is considered to be highlighted when it is selected.
 608:             if (menuItem.isSelected() || menuItem.getModel().isArmed() && 
 609:                 (menuItem.getParent() instanceof MenuElement)) 
 610:               g.setColor(selectionForeground);
 611:             else
 612:               g.setColor(menuItem.getForeground());
 613:           }
 614:     else
 615:       // FIXME: should fix this to use 'disabledForeground', but its
 616:       // default value in BasicLookAndFeel is null.      
 617:           
 618:           // FIXME: should there be different foreground colours for selected
 619:           // or deselected, when disabled?
 620:           g.setColor(Color.gray);
 621: 
 622:     int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
 623: 
 624:     if (mnemonicIndex != -1)
 625:       BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex,
 626:                                                    textRect.x,
 627:                                                    textRect.y
 628:                                                    + fm.getAscent());
 629:     else
 630:       BasicGraphicsUtils.drawString(g, text, 0, textRect.x,
 631:                                     textRect.y + fm.getAscent());
 632:       }
 633:   }
 634: 
 635:   /**
 636:    * This method uninstalls the components for this {@link JMenuItem}.
 637:    *
 638:    * @param menuItem The {@link JMenuItem} to uninstall components for.
 639:    */
 640:   protected void uninstallComponents(JMenuItem menuItem)
 641:   {
 642:     // FIXME: need to implement
 643:   }
 644: 
 645:   /**
 646:    * This method uninstalls the defaults and sets any objects created during
 647:    * install to null
 648:    */
 649:   protected void uninstallDefaults()
 650:   {
 651:     menuItem.setForeground(null);
 652:     menuItem.setBackground(null);
 653:     menuItem.setBorder(null);
 654:     menuItem.setMargin(null);
 655:     menuItem.setBackground(null);
 656:     menuItem.setBorder(null);
 657:     menuItem.setFont(null);
 658:     menuItem.setForeground(null);
 659:     menuItem.setMargin(null);
 660:     acceleratorFont = null;
 661:     acceleratorForeground = null;
 662:     acceleratorSelectionForeground = null;
 663:     arrowIcon = null;
 664:     selectionBackground = null;
 665:     selectionForeground = null;
 666:     acceleratorDelimiter = null;
 667:   }
 668: 
 669:   /**
 670:    * Uninstalls any keyboard actions.
 671:    */
 672:   protected void uninstallKeyboardActions()
 673:   {
 674:     // FIXME: need to implement
 675:   }
 676: 
 677:   /**
 678:    * Unregisters all the listeners that this UI delegate was using.
 679:    */
 680:   protected void uninstallListeners()
 681:   {
 682:     menuItem.removeMouseListener(mouseInputListener);
 683:     menuItem.removeMenuDragMouseListener(menuDragMouseListener);
 684:     menuItem.removeMenuKeyListener(menuKeyListener);
 685:     menuItem.removePropertyChangeListener(propertyChangeListener);
 686:   }
 687: 
 688:   /**
 689:    * Performs the opposite of installUI. Any properties or resources that need
 690:    * to be cleaned up will be done now. It will also uninstall any listeners
 691:    * it has. In addition, any properties of this UI will be nulled.
 692:    *
 693:    * @param c The {@link JComponent} that is having this UI uninstalled.
 694:    */
 695:   public void uninstallUI(JComponent c)
 696:   {
 697:     uninstallListeners();
 698:     uninstallDefaults();
 699:     uninstallComponents(menuItem);
 700:     menuItem = null;
 701:   }
 702: 
 703:   /**
 704:    * This method calls paint.
 705:    *
 706:    * @param g The graphics context used to paint this menu item
 707:    * @param c The menu item to paint
 708:    */
 709:   public void update(Graphics g, JComponent c)
 710:   {
 711:     paint(g, c);
 712:   }
 713: 
 714:   /**
 715:    * Return text representation of the specified accelerator
 716:    *
 717:    * @param accelerator Accelerator for which to return string representation
 718:    *
 719:    * @return $String$ Text representation of the given accelerator
 720:    */
 721:   private String getAcceleratorText(KeyStroke accelerator)
 722:   {
 723:     // convert keystroke into string format
 724:     String modifiersText = "";
 725:     int modifiers = accelerator.getModifiers();
 726:     char keyChar = accelerator.getKeyChar();
 727:     int keyCode = accelerator.getKeyCode();
 728: 
 729:     if (modifiers != 0)
 730:       modifiersText = KeyEvent.getKeyModifiersText(modifiers)
 731:                       + acceleratorDelimiter;
 732: 
 733:     if (keyCode == KeyEvent.VK_UNDEFINED)
 734:       return modifiersText + keyChar;
 735:     else
 736:       return modifiersText + KeyEvent.getKeyText(keyCode);
 737:   }
 738: 
 739:   /**
 740:    * Calculates and return rectange in which accelerator should be displayed
 741:    *
 742:    * @param accelerator accelerator for which to return the display rectangle
 743:    * @param fm The font metrics used to measure the text
 744:    *
 745:    * @return $Rectangle$ reactangle which will be used to display accelerator
 746:    */
 747:   private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm)
 748:   {
 749:     int width = fm.stringWidth(getAcceleratorText(accelerator));
 750:     int height = fm.getHeight();
 751:     return new Rectangle(0, 0, width, height);
 752:   }
 753: 
 754:   /**
 755:    * Paints accelerator inside menu item
 756:    *
 757:    * @param g The graphics context used to paint the border
 758:    * @param menuItem Menu item for which to draw accelerator
 759:    * @param acceleratorRect rectangle representing position
 760:    * of the accelerator relative to the menu item
 761:    * @param acceleratorText accelerator's text
 762:    */
 763:   private void paintAccelerator(Graphics g, JMenuItem menuItem,
 764:                                 Rectangle acceleratorRect,
 765:                                 String acceleratorText)
 766:   {
 767:     g.setFont(acceleratorFont);
 768:     FontMetrics fm = g.getFontMetrics(acceleratorFont);
 769: 
 770:     if (menuItem.isEnabled())
 771:       g.setColor(acceleratorForeground);
 772:     else
 773:       // FIXME: should fix this to use 'disabledForeground', but its
 774:       // default value in BasicLookAndFeel is null.
 775:       g.setColor(Color.gray);
 776: 
 777:     BasicGraphicsUtils.drawString(g, acceleratorText, 0, acceleratorRect.x,
 778:                                   acceleratorRect.y + fm.getAscent());
 779:   }
 780: 
 781:   /**
 782:    * This class handles mouse events occuring inside the menu item.
 783:    * Most of the events are forwarded for processing to MenuSelectionManager
 784:    * of the current menu hierarchy.
 785:    *
 786:    */
 787:   protected class MouseInputHandler implements MouseInputListener
 788:   {
 789:     /**
 790:      * Creates a new MouseInputHandler object.
 791:      */
 792:     protected MouseInputHandler()
 793:     {
 794:     }
 795: 
 796:     /**
 797:      * This method is called when mouse is clicked on the menu item.
 798:      * It forwards this event to MenuSelectionManager.
 799:      *
 800:      * @param e A {@link MouseEvent}.
 801:      */
 802:     public void mouseClicked(MouseEvent e)
 803:     {
 804:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 805:       manager.processMouseEvent(e);
 806:     }
 807: 
 808:     /**
 809:      * This method is called when mouse is dragged inside the menu item.
 810:      * It forwards this event to MenuSelectionManager.
 811:      *
 812:      * @param e A {@link MouseEvent}.
 813:      */
 814:     public void mouseDragged(MouseEvent e)
 815:     {
 816:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 817:       manager.processMouseEvent(e);
 818:     }
 819: 
 820:     /**
 821:      * This method is called when mouse enters menu item.
 822:      * When this happens menu item is considered to be selected and selection path
 823:      * in MenuSelectionManager is set. This event is also forwarded to MenuSelection
 824:      * Manager for further processing.
 825:      *
 826:      * @param e A {@link MouseEvent}.
 827:      */
 828:     public void mouseEntered(MouseEvent e)
 829:     {
 830:       Component source = (Component) e.getSource();
 831:       if (source.getParent() instanceof MenuElement)
 832:         {
 833:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 834:       manager.setSelectedPath(getPath());
 835:       manager.processMouseEvent(e);
 836:         }
 837:     }
 838: 
 839:     /**
 840:      * This method is called when mouse exits menu item. The event is
 841:      * forwarded to MenuSelectionManager for processing.
 842:      *
 843:      * @param e A {@link MouseEvent}.
 844:      */
 845:     public void mouseExited(MouseEvent e)
 846:     {
 847:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 848:       manager.processMouseEvent(e);
 849:     }
 850: 
 851:     /**
 852:      * This method is called when mouse is inside the menu item.
 853:      * This event is forwarder to MenuSelectionManager for further processing.
 854:      *
 855:      * @param e A {@link MouseEvent}.
 856:      */
 857:     public void mouseMoved(MouseEvent e)
 858:     {
 859:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 860:       manager.processMouseEvent(e);
 861:     }
 862: 
 863:     /**
 864:      * This method is called when mouse is pressed. This event is forwarded to
 865:      * MenuSelectionManager for further processing.
 866:      *
 867:      * @param e A {@link MouseEvent}.
 868:      */
 869:     public void mousePressed(MouseEvent e)
 870:     {
 871:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 872:       manager.processMouseEvent(e);
 873:     }
 874: 
 875:     /**
 876:      * This method is called when mouse is released. If the mouse is released
 877:      * inside this menuItem, then this menu item is considered to be chosen and
 878:      * the menu hierarchy should be closed.
 879:      *
 880:      * @param e A {@link MouseEvent}.
 881:      */
 882:     public void mouseReleased(MouseEvent e)
 883:     {
 884:       Rectangle size = menuItem.getBounds();
 885:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 886:       if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0
 887:           && e.getY() < size.height)
 888:         {
 889:       manager.clearSelectedPath();
 890:       menuItem.doClick();
 891:         }
 892: 
 893:       else
 894:     manager.processMouseEvent(e);
 895:     }
 896:   }
 897: 
 898:   /**
 899:    * This class handles mouse dragged events.
 900:    */
 901:   protected class MenuDragMouseHandler implements MenuDragMouseListener
 902:   {
 903:     /**
 904:      * Tbis method is invoked when mouse is dragged over the menu item.
 905:      *
 906:      * @param e The MenuDragMouseEvent
 907:      */
 908:     public void menuDragMouseDragged(MenuDragMouseEvent e)
 909:     {
 910:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 911:       manager.setSelectedPath(e.getPath());
 912:     }
 913: 
 914:     /**
 915:      * Tbis method is invoked when mouse enters the menu item while it is
 916:      * being dragged.
 917:      *
 918:      * @param e The MenuDragMouseEvent
 919:      */
 920:     public void menuDragMouseEntered(MenuDragMouseEvent e)
 921:     {
 922:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 923:       manager.setSelectedPath(e.getPath());
 924:     }
 925: 
 926:     /**
 927:      * Tbis method is invoked when mouse exits the menu item while
 928:      * it is being dragged
 929:      *
 930:      * @param e The MenuDragMouseEvent
 931:      */
 932:     public void menuDragMouseExited(MenuDragMouseEvent e)
 933:     {
 934:     }
 935: 
 936:     /**
 937:      * Tbis method is invoked when mouse was dragged and released
 938:      * inside the menu item.
 939:      *
 940:      * @param e The MenuDragMouseEvent
 941:      */
 942:     public void menuDragMouseReleased(MenuDragMouseEvent e)
 943:     {
 944:       MenuElement[] path = e.getPath();
 945: 
 946:       if (path[path.length - 1] instanceof JMenuItem)
 947:     ((JMenuItem) path[path.length - 1]).doClick();
 948: 
 949:       MenuSelectionManager manager = MenuSelectionManager.defaultManager();
 950:       manager.clearSelectedPath();
 951:     }
 952:   }
 953: 
 954:   /**
 955:    * This class handles key events occuring when menu item is visible on the
 956:    * screen.
 957:    */
 958:   protected class MenuKeyHandler implements MenuKeyListener
 959:   {
 960:     /**
 961:      * This method is invoked when key has been pressed
 962:      *
 963:      * @param e A {@link MenuKeyEvent}.
 964:      */
 965:     public void menuKeyPressed(MenuKeyEvent e)
 966:     {
 967:     }
 968: 
 969:     /**
 970:      * This method is invoked when key has been pressed
 971:      *
 972:      * @param e A {@link MenuKeyEvent}.
 973:      */
 974:     public void menuKeyReleased(MenuKeyEvent e)
 975:     {
 976:     }
 977: 
 978:     /**
 979:      * This method is invoked when key has been typed
 980:      * It handles the mnemonic key for the menu item.
 981:      *
 982:      * @param e A {@link MenuKeyEvent}.
 983:      */
 984:     public void menuKeyTyped(MenuKeyEvent e)
 985:     {
 986:     }
 987:   }
 988: 
 989:   /**
 990:    * Helper class that listens for changes to the properties of the {@link
 991:    * JMenuItem}.
 992:    */
 993:   protected class PropertyChangeHandler implements PropertyChangeListener
 994:   {
 995:     /**
 996:      * This method is called when one of the menu item's properties change.
 997:      *
 998:      * @param evt A {@link PropertyChangeEvent}.
 999:      */
1000:     public void propertyChange(PropertyChangeEvent evt)
1001:     {
1002:       menuItem.revalidate();
1003:       menuItem.repaint();
1004:     }
1005:   }
1006: }