OSDN Git Service

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