1 /* JPopupMenu.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;
40 
41 import gnu.java.lang.CPStringBuilder;
42 
43 import java.awt.Component;
44 import java.awt.Dimension;
45 import java.awt.Insets;
46 import java.awt.Point;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseEvent;
49 import java.beans.PropertyChangeEvent;
50 import java.beans.PropertyChangeListener;
51 import java.util.ArrayList;
52 import java.util.EventListener;
53 
54 import javax.accessibility.Accessible;
55 import javax.accessibility.AccessibleContext;
56 import javax.accessibility.AccessibleRole;
57 import javax.swing.event.MenuKeyListener;
58 import javax.swing.event.PopupMenuEvent;
59 import javax.swing.event.PopupMenuListener;
60 import javax.swing.plaf.PopupMenuUI;
61 
62 /**
63  * JPopupMenu is a container that is used to display popup menu's menu
64  * items. By default JPopupMenu is a lightweight container, however if it
65  * is the case that JPopupMenu's bounds are outside of main window, then
66  * heawyweight container will be used to display menu items. It is also
67  * possible to change JPopupMenu's default  behavior and set JPopupMenu
68  * to always use heavyweight container.
69  *
70  * JPopupMenu can be displayed anywhere; it is a floating free popup menu.
71  * However before JPopupMenu is diplayed, its invoker property should be set.
72  * JPopupMenu's invoker is a component relative to which popup menu is
73  * displayed.
74  *
75  * JPopupMenu fires PopupMenuEvents to its registered listeners. Whenever
76  * JPopupMenu becomes visible on the screen then PopupMenuEvent indicating
77  * that popup menu became visible will be fired. In the case when
78  * JPopupMenu becomes invisible or cancelled without selection, then
79  * popupMenuBecomeInvisible() or popupMenuCancelled() methods of
80  * PopupMenuListeners will be invoked.
81  *
82  * JPopupMenu also fires PropertyChangeEvents when its bound properties
83  * change.In addittion to inheritted bound properties, JPopupMenu has
84  * 'visible' bound property. When JPopupMenu becomes visible/invisible on
85  * the screen it fires PropertyChangeEvents to its registered
86  * PropertyChangeListeners.
87  */
88 public class JPopupMenu extends JComponent implements Accessible, MenuElement
89 {
90   private static final long serialVersionUID = -8336996630009646009L;
91 
92   /* indicates if popup's menu border should be painted*/
93   private boolean borderPainted = true;
94 
95   /** Flag indicating whether lightweight, mediumweight or heavyweight popup
96      is used to display menu items.
97 
98      These are the possible cases:
99 
100      1. if DefaultLightWeightPopupEnabled true
101          (i)  use lightweight container if popup feets inside top-level window
102          (ii) only use heavyweight container (JDialog) if popup doesn't fit.
103 
104      2. if DefaultLightWeightPopupEnabled false
105          (i) if popup fits, use awt.Panel (mediumWeight)
106          (ii) if popup doesn't fit, use JDialog (heavyWeight)
107   */
108   private static boolean DefaultLightWeightPopupEnabled = true;
109 
110   /* Component that invokes popup menu. */
111   transient Component invoker;
112 
113   /* Label for this popup menu. It is not used in most of the look and feel themes. */
114   private String label;
115 
116   /*Amount of space between menuItem's in JPopupMenu and JPopupMenu's border */
117   private Insets margin;
118 
119   /** Indicates whether ligthWeight container can be used to display popup
120      menu. This flag is the same as DefaultLightWeightPopupEnabled, but setting
121      this flag can change popup menu after creation of the object */
122   private boolean lightWeightPopupEnabled;
123 
124   /** SelectionModel that keeps track of menu selection. */
125   protected SingleSelectionModel selectionModel;
126 
127   /* Popup that is used to display JPopupMenu */
128   private transient Popup popup;
129 
130   /**
131    * Location of the popup, X coordinate.
132    */
133   private int popupLocationX;
134 
135   /**
136    * Location of the popup, Y coordinate.
137    */
138   private int popupLocationY;
139 
140   /* Field indicating if popup menu is visible or not */
141   private boolean visible = false;
142 
143   /**
144    * Creates a new JPopupMenu object.
145    */
JPopupMenu()146   public JPopupMenu()
147   {
148     this(null);
149   }
150 
151   /**
152    * Creates a new JPopupMenu with specified label
153    *
154    * @param label Label for popup menu.
155    */
JPopupMenu(String label)156   public JPopupMenu(String label)
157   {
158     lightWeightPopupEnabled = getDefaultLightWeightPopupEnabled();
159     setLabel(label);
160     setSelectionModel(new DefaultSingleSelectionModel());
161     super.setVisible(false);
162     updateUI();
163   }
164 
165   /**
166   * Adds given menu item to the popup menu
167   *
168   * @param item menu item to add to the popup menu
169   *
170   * @return menu item that was added to the popup menu
171   */
add(JMenuItem item)172   public JMenuItem add(JMenuItem item)
173   {
174     this.insert(item, -1);
175     return item;
176   }
177 
178   /**
179    * Constructs menu item with a specified label and adds it to
180    * popup menu
181    *
182    * @param text label for the menu item to be added
183    *
184    * @return constructed menu item that was added to the popup menu
185    */
add(String text)186   public JMenuItem add(String text)
187   {
188     JMenuItem item = new JMenuItem(text);
189     return add(item);
190   }
191 
192   /**
193    * Constructs menu item associated with the specified action
194    * and adds it to the popup menu
195    *
196    * @param action Action for the new menu item
197    *
198    * @return menu item that was added to the menu
199    */
add(Action action)200   public JMenuItem add(Action action)
201   {
202     JMenuItem item = createActionComponent(action);
203 
204     if (action != null)
205       action.addPropertyChangeListener(createActionChangeListener(item));
206 
207     return add(item);
208   }
209 
210   /**
211    * Revomes component at the given index from the menu.
212    *
213    * @param index index of the component that will be removed in the menu
214    */
remove(int index)215   public void remove(int index)
216   {
217     super.remove(index);
218     revalidate();
219   }
220 
221   /**
222    * Create menu item associated with the given action
223    * and inserts it into the popup menu at the specified index
224    *
225    * @param action Action for the new menu item
226    * @param index index in the popup menu at which to insert new menu item.
227    */
insert(Action action, int index)228   public void insert(Action action, int index)
229   {
230     JMenuItem item = new JMenuItem(action);
231     this.insert(item, index);
232   }
233 
234   /**
235    * Insert given component to the popup menu at the
236    * specified index
237    *
238    * @param component Component to insert
239    * @param index Index at which to insert given component
240    */
insert(Component component, int index)241   public void insert(Component component, int index)
242   {
243     super.add(component, index);
244   }
245 
246   /**
247    * Returns flag indicating if newly created JPopupMenu will use
248    * heavyweight or lightweight container to display its menu items
249    *
250    * @return true if JPopupMenu will use lightweight container to display
251    * menu items by default, and false otherwise.
252    */
getDefaultLightWeightPopupEnabled()253   public static boolean getDefaultLightWeightPopupEnabled()
254   {
255     return DefaultLightWeightPopupEnabled;
256   }
257 
258   /**
259    * Sets whether JPopupMenu should use ligthWeight container to
260    * display it menu items by default
261    *
262    * @param enabled true if JPopupMenu should use lightweight container
263    * for displaying its menu items, and false otherwise.
264    */
setDefaultLightWeightPopupEnabled(boolean enabled)265   public static void setDefaultLightWeightPopupEnabled(boolean enabled)
266   {
267     DefaultLightWeightPopupEnabled = enabled;
268   }
269 
270   /**
271    * This method returns the UI used to display the JPopupMenu.
272    *
273    * @return The UI used to display the JPopupMenu.
274    */
getUI()275   public PopupMenuUI getUI()
276   {
277     return (PopupMenuUI) ui;
278   }
279 
280   /**
281    * Set the "UI" property of the menu item, which is a look and feel class
282    * responsible for handling popupMenu's input events and painting it.
283    *
284    * @param ui The new "UI" property
285    */
setUI(PopupMenuUI ui)286   public void setUI(PopupMenuUI ui)
287   {
288     super.setUI(ui);
289   }
290 
291   /**
292    * This method sets this menuItem's UI to the UIManager's default for the
293    * current look and feel.
294    */
updateUI()295   public void updateUI()
296   {
297     setUI((PopupMenuUI) UIManager.getUI(this));
298   }
299 
300   /**
301    * This method returns a name to identify which look and feel class will be
302    * the UI delegate for the menuItem.
303    *
304    * @return The Look and Feel classID. "PopupMenuUI"
305    */
getUIClassID()306   public String getUIClassID()
307   {
308     return "PopupMenuUI";
309   }
310 
311   /**
312    * Returns selectionModel used by this popup menu to keep
313    * track of the selection.
314    *
315    * @return popup menu's selection model
316    */
getSelectionModel()317   public SingleSelectionModel getSelectionModel()
318   {
319     return selectionModel;
320   }
321 
322   /**
323    * Sets selection model for this popup menu
324    *
325    * @param model new selection model of this popup menu
326    */
setSelectionModel(SingleSelectionModel model)327   public void setSelectionModel(SingleSelectionModel model)
328   {
329         selectionModel = model;
330   }
331 
332   /**
333    * Creates new menu item associated with a given action.
334    *
335    * @param action Action used to create new menu item
336    *
337    * @return new created menu item associated with a given action.
338    */
createActionComponent(Action action)339   protected JMenuItem createActionComponent(Action action)
340   {
341     return new JMenuItem(action);
342   }
343 
344   /**
345    * Creates PropertyChangeListener that listens to PropertyChangeEvents
346    * occuring in the Action associated with given menu item in this popup menu.
347    *
348    * @param item MenuItem
349    *
350    * @return The PropertyChangeListener
351    */
createActionChangeListener(JMenuItem item)352   protected PropertyChangeListener createActionChangeListener(JMenuItem item)
353   {
354     return new ActionChangeListener();
355   }
356 
357   /**
358    * Returns true if this popup menu will display its menu item in
359    * a lightweight container and false otherwise.
360    *
361    * @return true if this popup menu will display its menu items
362    * in a lightweight container and false otherwise.
363    */
isLightWeightPopupEnabled()364   public boolean isLightWeightPopupEnabled()
365   {
366     return lightWeightPopupEnabled;
367   }
368 
369   /**
370    * DOCUMENT ME!
371    *
372    * @param enabled DOCUMENT ME!
373    */
setLightWeightPopupEnabled(boolean enabled)374   public void setLightWeightPopupEnabled(boolean enabled)
375   {
376     lightWeightPopupEnabled = enabled;
377   }
378 
379   /**
380    * Returns label for this popup menu
381    *
382    * @return label for this popup menu
383    */
getLabel()384   public String getLabel()
385   {
386     return label;
387   }
388 
389   /**
390    * Sets label for this popup menu. This method fires PropertyChangeEvent
391    * when the label property is changed. Please note that most
392    * of the Look & Feel will ignore this property.
393    *
394    * @param label label for this popup menu
395    */
setLabel(String label)396   public void setLabel(String label)
397   {
398     if (label != this.label)
399       {
400         String oldLabel = this.label;
401         this.label = label;
402         firePropertyChange("label", oldLabel, label);
403       }
404   }
405 
406   /**
407    * Adds separator to this popup menu
408    */
addSeparator()409   public void addSeparator()
410   {
411     // insert separator at the end of the list of menu items
412     this.insert(new Separator(), -1);
413   }
414 
415   /**
416    * Adds a MenuKeyListener to the popup.
417    *
418    * @param l - the listener to add.
419    */
addMenuKeyListener(MenuKeyListener l)420   public void addMenuKeyListener(MenuKeyListener l)
421   {
422     listenerList.add(MenuKeyListener.class, l);
423   }
424 
425   /**
426    * Removes a MenuKeyListener from the popup.
427    *
428    * @param l - the listener to remove.
429    */
removeMenuKeyListener(MenuKeyListener l)430   public void removeMenuKeyListener(MenuKeyListener l)
431   {
432     listenerList.remove(MenuKeyListener.class, l);
433   }
434 
435   /**
436    * Returns array of getMenuKeyListeners that are listening to JPopupMenu.
437    *
438    * @return array of getMenuKeyListeners that are listening to JPopupMenu
439    */
getMenuKeyListeners()440   public MenuKeyListener[] getMenuKeyListeners()
441   {
442     return ((MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class));
443   }
444 
445   /**
446    * Adds popupMenuListener to listen for PopupMenuEvents fired
447    * by the JPopupMenu
448    *
449    * @param listener PopupMenuListener to add to JPopupMenu
450    */
addPopupMenuListener(PopupMenuListener listener)451   public void addPopupMenuListener(PopupMenuListener listener)
452   {
453     listenerList.add(PopupMenuListener.class, listener);
454   }
455 
456   /**
457    * Removes PopupMenuListener from JPopupMenu's list of listeners
458    *
459    * @param listener PopupMenuListener which needs to be removed
460    */
removePopupMenuListener(PopupMenuListener listener)461   public void removePopupMenuListener(PopupMenuListener listener)
462   {
463     listenerList.remove(PopupMenuListener.class, listener);
464   }
465 
466   /**
467    * Returns array of PopupMenuListeners that are listening to JPopupMenu
468    *
469    * @return Array of PopupMenuListeners that are listening to JPopupMenu
470    */
getPopupMenuListeners()471   public PopupMenuListener[] getPopupMenuListeners()
472   {
473     return ((PopupMenuListener[]) listenerList.getListeners(PopupMenuListener.class));
474   }
475 
476   /**
477    * This method calls popupMenuWillBecomeVisible() of popup menu's
478    * PopupMenuListeners. This method is invoked just before popup menu
479    * will appear on the screen.
480    */
firePopupMenuWillBecomeVisible()481   protected void firePopupMenuWillBecomeVisible()
482   {
483     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
484 
485     for (int i = 0; i < ll.length; i++)
486       ((PopupMenuListener) ll[i]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
487   }
488 
489   /**
490    * This method calls popupMenuWillBecomeInvisible() of popup
491    * menu's PopupMenuListeners. This method is invoked just before popup
492    * menu will disappear from the screen
493    */
firePopupMenuWillBecomeInvisible()494   protected void firePopupMenuWillBecomeInvisible()
495   {
496     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
497 
498     for (int i = 0; i < ll.length; i++)
499       ((PopupMenuListener) ll[i]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
500   }
501 
502   /**
503    * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
504    * This method is invoked just before popup menu is cancelled. This happens
505    * when popup menu is closed without selecting any of its menu items. This
506    * usually happens when the top-level window is resized or moved.
507    */
firePopupMenuCanceled()508   protected void firePopupMenuCanceled()
509   {
510     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
511 
512     for (int i = 0; i < ll.length; i++)
513       ((PopupMenuListener) ll[i]).popupMenuCanceled(new PopupMenuEvent(this));
514   }
515 
516   /**
517    * This methods sets popup menu's size to its' preferred size. If the
518    * popup menu's size is previously set it will be ignored.
519    */
pack()520   public void pack()
521   {
522     // Hook up this call so that it gets executed on the event thread in order
523     // to avoid synchronization problems when calling the layout manager.
524     if (! SwingUtilities.isEventDispatchThread())
525       {
526         SwingUtilities.invokeLater(new Runnable()
527           {
528             public void run()
529             {
530               show();
531             }
532           });
533       }
534 
535     setSize(getPreferredSize());
536   }
537 
538   /**
539    * Return visibility of the popup menu
540    *
541    * @return true if popup menu is visible on the screen and false otherwise.
542    */
isVisible()543   public boolean isVisible()
544   {
545     return visible;
546   }
547 
548   /**
549    * Sets visibility property of this popup menu. If the property is
550    * set to true then popup menu will be dispayed and popup menu will
551    * hide itself if visible property is set to false.
552    *
553    * @param visible true if popup menu will become visible and false otherwise.
554    */
setVisible(final boolean visible)555   public void setVisible(final boolean visible)
556   {
557     // Hook up this call so that it gets executed on the event thread in order
558     // to avoid synchronization problems when calling the layout manager.
559     if (! SwingUtilities.isEventDispatchThread())
560       {
561         SwingUtilities.invokeLater(new Runnable()
562           {
563             public void run()
564             {
565               setVisible(visible);
566             }
567           });
568       }
569 
570     if (visible == isVisible())
571       return;
572 
573     boolean old = isVisible();
574     this.visible = visible;
575     if (old != isVisible())
576       {
577         if (visible)
578           {
579             if (invoker != null && !(invoker instanceof JMenu))
580               {
581                 MenuElement[] menuEls;
582                 if (getSubElements().length > 0)
583                   {
584                     menuEls = new MenuElement[2];
585                     menuEls[0] = this;
586                     menuEls[1] = getSubElements()[0];
587                 }
588                 else
589                   {
590                     menuEls = new MenuElement[1];
591                     menuEls[0] = this;
592                   }
593                 MenuSelectionManager.defaultManager().setSelectedPath(menuEls);
594               }
595             firePopupMenuWillBecomeVisible();
596             PopupFactory pf = PopupFactory.getSharedInstance();
597             pack();
598             popup = pf.getPopup(invoker, this, popupLocationX, popupLocationY);
599             popup.show();
600           }
601         else
602           {
603             getSelectionModel().clearSelection();
604             firePopupMenuWillBecomeInvisible();
605             popup.hide();
606           }
607         firePropertyChange("visible", old, isVisible());
608       }
609   }
610 
611   /**
612    * Sets location of the popup menu.
613    *
614    * @param x X coordinate of the popup menu's location
615    * @param y Y coordinate of the popup menu's location
616    */
setLocation(int x, int y)617   public void setLocation(int x, int y)
618   {
619     popupLocationX = x;
620     popupLocationY = y;
621     // Handle the case when the popup is already showing. In this case we need
622     // to fetch a new popup from PopupFactory and use this. See the general
623     // contract of the PopupFactory.
624   }
625 
626   /**
627    * Returns popup menu's invoker.
628    *
629    * @return popup menu's invoker
630    */
getInvoker()631   public Component getInvoker()
632   {
633     return invoker;
634   }
635 
636   /**
637    * Sets popup menu's invoker.
638    *
639    * @param component The new invoker of this popup menu
640    */
setInvoker(Component component)641   public void setInvoker(Component component)
642   {
643     invoker = component;
644   }
645 
646   /**
647    * This method displays JPopupMenu on the screen at the specified
648    * location. Note that x and y coordinates given to this method
649    * should be expressed in terms of the popup menus' invoker.
650    *
651    * @param component Invoker for this popup menu
652    * @param x x-coordinate of the popup menu relative to the specified invoker
653    * @param y y-coordiate of the popup menu relative to the specified invoker
654    */
show(Component component, int x, int y)655   public void show(Component component, int x, int y)
656   {
657     if (component.isShowing())
658       {
659         setInvoker(component);
660         Point p = new Point(x, y);
661         SwingUtilities.convertPointToScreen(p, component);
662         setLocation(p.x, p.y);
663         setVisible(true);
664       }
665   }
666 
667   /**
668    * Returns component located at the specified index in the popup menu
669    *
670    * @param index index of the component to return
671    *
672    * @return component located at the specified index in the popup menu
673    *
674    * @deprecated Replaced by getComponent(int)
675    */
getComponentAtIndex(int index)676   public Component getComponentAtIndex(int index)
677   {
678     return getComponent(index);
679   }
680 
681   /**
682    * Returns index of the specified component in the popup menu
683    *
684    * @param component Component to look for
685    *
686    * @return index of the specified component in the popup menu
687    */
getComponentIndex(Component component)688   public int getComponentIndex(Component component)
689   {
690     Component[] items = getComponents();
691 
692     for (int i = 0; i < items.length; i++)
693       {
694         if (items[i].equals(component))
695           return i;
696       }
697 
698     return -1;
699   }
700 
701   /**
702    * Sets size of the popup
703    *
704    * @param size Dimensions representing new size of the popup menu
705    */
setPopupSize(Dimension size)706   public void setPopupSize(Dimension size)
707   {
708     super.setSize(size);
709   }
710 
711   /**
712    * Sets size of the popup menu
713    *
714    * @param width width for the new size
715    * @param height height for the new size
716    */
setPopupSize(int width, int height)717   public void setPopupSize(int width, int height)
718   {
719     super.setSize(width, height);
720   }
721 
722   /**
723    * Selects specified component in this popup menu.
724    *
725    * @param selected component to select
726    */
setSelected(Component selected)727   public void setSelected(Component selected)
728   {
729     int index = getComponentIndex(selected);
730     selectionModel.setSelectedIndex(index);
731   }
732 
733   /**
734    * Checks if this popup menu paints its border.
735    *
736    * @return true if this popup menu paints its border and false otherwise.
737    */
isBorderPainted()738   public boolean isBorderPainted()
739   {
740     return borderPainted;
741   }
742 
743   /**
744    * Sets if the border of the popup menu should be
745    * painter or not.
746    *
747    * @param painted true if the border should be painted and false otherwise
748    */
setBorderPainted(boolean painted)749   public void setBorderPainted(boolean painted)
750   {
751     borderPainted = painted;
752   }
753 
754   /**
755    * Returns margin for this popup menu.
756    *
757    * @return margin for this popup menu.
758    */
getMargin()759   public Insets getMargin()
760   {
761     return margin;
762   }
763 
764   /**
765    * A string that describes this JPopupMenu. Normally only used
766    * for debugging.
767    *
768    * @return A string describing this JMenuItem
769    */
paramString()770   protected String paramString()
771   {
772     CPStringBuilder sb = new CPStringBuilder();
773     sb.append(super.paramString());
774     sb.append(",label=");
775     if (getLabel() != null)
776       sb.append(getLabel());
777     sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
778     sb.append(",margin=");
779     if (getMargin() != null)
780       sb.append(margin);
781     sb.append(",paintBorder=").append(isBorderPainted());
782     return sb.toString();
783   }
784 
785   /**
786   * Process mouse events forwarded from MenuSelectionManager. This method
787   * doesn't do anything. It is here to conform to the MenuElement interface.
788   *
789   * @param event event forwarded from MenuSelectionManager
790   * @param path path to the menu element from which event was generated
791   * @param manager MenuSelectionManager for the current menu hierarchy
792   */
processMouseEvent(MouseEvent event, MenuElement[] path, MenuSelectionManager manager)793   public void processMouseEvent(MouseEvent event, MenuElement[] path,
794                                 MenuSelectionManager manager)
795   {
796     // Empty Implementation. This method is needed for the implementation
797     // of MenuElement interface
798   }
799 
800   /**
801    * Process key events forwarded from MenuSelectionManager. This method
802    * doesn't do anything. It is here to conform to the MenuElement interface.
803    *
804    * @param event event forwarded from MenuSelectionManager
805    * @param path path to the menu element from which event was generated
806    * @param manager MenuSelectionManager for the current menu hierarchy
807    *
808    */
processKeyEvent(KeyEvent event, MenuElement[] path, MenuSelectionManager manager)809   public void processKeyEvent(KeyEvent event, MenuElement[] path,
810                               MenuSelectionManager manager)
811   {
812     // Empty Implementation. This method is needed for the implementation
813     // of MenuElement interface
814   }
815 
816   /**
817    * Method of MenuElement Interface. It is invoked when
818    * popupMenu's selection has changed
819    *
820    * @param changed true if this popupMenu is part of current menu
821    * hierarchy and false otherwise.
822    */
menuSelectionChanged(boolean changed)823   public void menuSelectionChanged(boolean changed)
824   {
825     if (invoker instanceof JMenu)
826       {
827         // We need to special case this since the JMenu calculates the
828         // position etc of the popup.
829         JMenu menu = (JMenu) invoker;
830         menu.setPopupMenuVisible(changed);
831       }
832     else if (! changed)
833       setVisible(false);
834   }
835 
836   /**
837    * Return subcomonents of this popup menu. This method returns only
838    * components that implement the <code>MenuElement</code> interface.
839    *
840    * @return array of menu items belonging to this popup menu
841    */
getSubElements()842   public MenuElement[] getSubElements()
843   {
844     Component[] items = getComponents();
845     ArrayList subElements = new ArrayList();
846 
847     for (int i = 0; i < items.length; i++)
848       if (items[i] instanceof MenuElement)
849         subElements.add(items[i]);
850 
851     return (MenuElement[])
852       subElements.toArray(new MenuElement[subElements.size()]);
853   }
854 
855   /**
856    * Method of the MenuElement interface. Returns reference to itself.
857    *
858    * @return Returns reference to itself
859    */
getComponent()860   public Component getComponent()
861   {
862     return this;
863   }
864 
865   /**
866    * Checks if observing mouse event should trigger popup
867    * menu to show on the screen.
868    *
869    * @param event MouseEvent to check
870    *
871    * @return true if the observing mouse event is popup trigger and false otherwise
872    */
isPopupTrigger(MouseEvent event)873   public boolean isPopupTrigger(MouseEvent event)
874   {
875     return ((PopupMenuUI) getUI()).isPopupTrigger(event);
876   }
877 
878   /**
879    * DOCUMENT ME!
880    *
881    * @return DOCUMENT ME!
882    */
getAccessibleContext()883   public AccessibleContext getAccessibleContext()
884   {
885     if (accessibleContext == null)
886       accessibleContext = new AccessibleJPopupMenu();
887 
888     return accessibleContext;
889   }
890 
891   /**
892    * This is the separator that can be used in popup menu.
893    */
894   public static class Separator extends JSeparator
895   {
Separator()896     public Separator()
897     {
898       super();
899     }
900 
getUIClassID()901     public String getUIClassID()
902     {
903       return "PopupMenuSeparatorUI";
904     }
905   }
906 
907   /**
908    * Returns <code>true</code> if the component is guaranteed to be painted
909    * on top of others. This returns false by default and is overridden by
910    * components like JMenuItem, JPopupMenu and JToolTip to return true for
911    * added efficiency.
912    *
913    * @return <code>true</code> if the component is guaranteed to be painted
914    *         on top of others
915    */
onTop()916   boolean onTop()
917   {
918     return true;
919   }
920 
921   protected class AccessibleJPopupMenu extends AccessibleJComponent
922   {
923     private static final long serialVersionUID = 7423261328879849768L;
924 
AccessibleJPopupMenu()925     protected AccessibleJPopupMenu()
926     {
927       // Nothing to do here.
928     }
929 
getAccessibleRole()930     public AccessibleRole getAccessibleRole()
931     {
932       return AccessibleRole.POPUP_MENU;
933     }
934   }
935 
936   /* This class resizes popup menu and repaints popup menu appropriately if one
937    of item's action has changed */
938   private class ActionChangeListener implements PropertyChangeListener
939   {
propertyChange(PropertyChangeEvent evt)940     public void propertyChange(PropertyChangeEvent evt)
941     {
942       // We used to have a revalidate() and repaint() call here. However I think
943       // this is not needed. Instead, a new Popup has to be fetched from the
944       // PopupFactory and used here.
945     }
946   }
947 }
948