2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
41 import java
.awt
.Component
;
42 import java
.awt
.Dimension
;
43 import java
.awt
.Insets
;
44 import java
.awt
.Point
;
45 import java
.awt
.event
.KeyEvent
;
46 import java
.awt
.event
.MouseEvent
;
47 import java
.beans
.PropertyChangeEvent
;
48 import java
.beans
.PropertyChangeListener
;
49 import java
.util
.ArrayList
;
50 import java
.util
.EventListener
;
52 import javax
.accessibility
.Accessible
;
53 import javax
.accessibility
.AccessibleContext
;
54 import javax
.accessibility
.AccessibleRole
;
55 import javax
.swing
.event
.PopupMenuEvent
;
56 import javax
.swing
.event
.PopupMenuListener
;
57 import javax
.swing
.plaf
.PopupMenuUI
;
60 * JPopupMenu is a container that is used to display popup menu's menu
61 * items. By default JPopupMenu is a lightweight container, however if it
62 * is the case that JPopupMenu's bounds are outside of main window, then
63 * heawyweight container will be used to display menu items. It is also
64 * possible to change JPopupMenu's default behavior and set JPopupMenu
65 * to always use heavyweight container.
67 * JPopupMenu can be displayed anywhere; it is a floating free popup menu.
68 * However before JPopupMenu is diplayed, its invoker property should be set.
69 * JPopupMenu's invoker is a component relative to which popup menu is
72 * JPopupMenu fires PopupMenuEvents to its registered listeners. Whenever
73 * JPopupMenu becomes visible on the screen then PopupMenuEvent indicating
74 * that popup menu became visible will be fired. In the case when
75 * JPopupMenu becomes invisible or cancelled without selection, then
76 * popupMenuBecomeInvisible() or popupMenuCancelled() methods of
77 * PopupMenuListeners will be invoked.
79 * JPopupMenu also fires PropertyChangeEvents when its bound properties
80 * change.In addittion to inheritted bound properties, JPopupMenu has
81 * 'visible' bound property. When JPopupMenu becomes visible/invisible on
82 * the screen it fires PropertyChangeEvents to its registered
83 * PropertyChangeListeners.
85 public class JPopupMenu
extends JComponent
implements Accessible
, MenuElement
87 private static final long serialVersionUID
= -8336996630009646009L;
89 /* indicates if popup's menu border should be painted*/
90 private boolean borderPainted
= true;
92 /** Flag indicating whether lightweight, mediumweight or heavyweight popup
93 is used to display menu items.
95 These are the possible cases:
97 1. if DefaultLightWeightPopupEnabled true
98 (i) use lightweight container if popup feets inside top-level window
99 (ii) only use heavyweight container (JDialog) if popup doesn't fit.
101 2. if DefaultLightWeightPopupEnabled false
102 (i) if popup fits, use awt.Panel (mediumWeight)
103 (ii) if popup doesn't fit, use JDialog (heavyWeight)
105 private static boolean DefaultLightWeightPopupEnabled
= true;
107 /* Component that invokes popup menu. */
108 transient Component invoker
;
110 /* Label for this popup menu. It is not used in most of the look and feel themes. */
111 private String label
;
113 /*Amount of space between menuItem's in JPopupMenu and JPopupMenu's border */
114 private Insets margin
;
116 /** Indicates whether ligthWeight container can be used to display popup
117 menu. This flag is the same as DefaultLightWeightPopupEnabled, but setting
118 this flag can change popup menu after creation of the object */
119 private boolean lightWeightPopupEnabled
;
121 /** SelectionModel that keeps track of menu selection. */
122 private SingleSelectionModel selectionModel
;
124 /* Popup that is used to display JPopupMenu */
125 private transient Popup popup
;
128 * Location of the popup, X coordinate.
130 private int popupLocationX
;
133 * Location of the popup, Y coordinate.
135 private int popupLocationY
;
137 /* Field indicating if popup menu is visible or not */
138 private boolean visible
= false;
141 * Creates a new JPopupMenu object.
149 * Creates a new JPopupMenu with specified label
151 * @param label Label for popup menu.
153 public JPopupMenu(String label
)
155 lightWeightPopupEnabled
= getDefaultLightWeightPopupEnabled();
157 setSelectionModel(new DefaultSingleSelectionModel());
158 super.setVisible(false);
163 * Adds given menu item to the popup menu
165 * @param item menu item to add to the popup menu
167 * @return menu item that was added to the popup menu
169 public JMenuItem
add(JMenuItem item
)
171 this.insert(item
, -1);
176 * Constructs menu item with a specified label and adds it to
179 * @param text label for the menu item to be added
181 * @return constructed menu item that was added to the popup menu
183 public JMenuItem
add(String text
)
185 JMenuItem item
= new JMenuItem(text
);
190 * Constructs menu item associated with the specified action
191 * and adds it to the popup menu
193 * @param action Action for the new menu item
195 * @return menu item that was added to the menu
197 public JMenuItem
add(Action action
)
199 JMenuItem item
= createActionComponent(action
);
202 action
.addPropertyChangeListener(createActionChangeListener(item
));
208 * Revomes component at the given index from the menu.
210 * @param index index of the component that will be removed in the menu
212 public void remove(int index
)
219 * Create menu item associated with the given action
220 * and inserts it into the popup menu at the specified index
222 * @param action Action for the new menu item
223 * @param index index in the popup menu at which to insert new menu item.
225 public void insert(Action action
, int index
)
227 JMenuItem item
= new JMenuItem(action
);
228 this.insert(item
, index
);
232 * Insert given component to the popup menu at the
235 * @param component Component to insert
236 * @param index Index at which to insert given component
238 public void insert(Component component
, int index
)
240 super.add(component
, index
);
244 * Returns flag indicating if newly created JPopupMenu will use
245 * heavyweight or lightweight container to display its menu items
247 * @return true if JPopupMenu will use lightweight container to display
248 * menu items by default, and false otherwise.
250 public static boolean getDefaultLightWeightPopupEnabled()
252 return DefaultLightWeightPopupEnabled
;
256 * Sets whether JPopupMenu should use ligthWeight container to
257 * display it menu items by default
259 * @param enabled true if JPopupMenu should use lightweight container
260 * for displaying its menu items, and false otherwise.
262 public static void setDefaultLightWeightPopupEnabled(boolean enabled
)
264 DefaultLightWeightPopupEnabled
= enabled
;
268 * This method returns the UI used to display the JPopupMenu.
270 * @return The UI used to display the JPopupMenu.
272 public PopupMenuUI
getUI()
274 return (PopupMenuUI
) ui
;
278 * Set the "UI" property of the menu item, which is a look and feel class
279 * responsible for handling popupMenu's input events and painting it.
281 * @param ui The new "UI" property
283 public void setUI(PopupMenuUI ui
)
289 * This method sets this menuItem's UI to the UIManager's default for the
290 * current look and feel.
292 public void updateUI()
294 setUI((PopupMenuUI
) UIManager
.getUI(this));
299 * This method returns a name to identify which look and feel class will be
300 * the UI delegate for the menuItem.
302 * @return The Look and Feel classID. "PopupMenuUI"
304 public String
getUIClassID()
306 return "PopupMenuUI";
310 * Returns selectionModel used by this popup menu to keep
311 * track of the selection.
313 * @return popup menu's selection model
315 public SingleSelectionModel
getSelectionModel()
317 return selectionModel
;
321 * Sets selection model for this popup menu
323 * @param model new selection model of this popup menu
325 public void setSelectionModel(SingleSelectionModel model
)
327 selectionModel
= model
;
331 * Creates new menu item associated with a given action.
333 * @param action Action used to create new menu item
335 * @return new created menu item associated with a given action.
337 protected JMenuItem
createActionComponent(Action action
)
339 return new JMenuItem(action
);
343 * Creates PropertyChangeListener that listens to PropertyChangeEvents
344 * occuring in the Action associated with given menu item in this popup menu.
346 * @param item MenuItem
348 * @return The PropertyChangeListener
350 protected PropertyChangeListener
createActionChangeListener(JMenuItem item
)
352 return new ActionChangeListener();
356 * Returns true if this popup menu will display its menu item in
357 * a lightweight container and false otherwise.
359 * @return true if this popup menu will display its menu items
360 * in a lightweight container and false otherwise.
362 public boolean isLightWeightPopupEnabled()
364 return lightWeightPopupEnabled
;
370 * @param enabled DOCUMENT ME!
372 public void setLightWeightPopupEnabled(boolean enabled
)
374 lightWeightPopupEnabled
= enabled
;
378 * Returns label for this popup menu
380 * @return label for this popup menu
382 public String
getLabel()
388 * Sets label for this popup menu. This method fires PropertyChangeEvent
389 * when the label property is changed. Please note that most
390 * of the Look & Feel will ignore this property.
392 * @param label label for this popup menu
394 public void setLabel(String label
)
396 if (label
!= this.label
)
398 String oldLabel
= this.label
;
400 firePropertyChange("label", oldLabel
, label
);
405 * Adds separator to this popup menu
407 public void addSeparator()
409 // insert separator at the end of the list of menu items
410 this.insert(new Separator(), -1);
414 * Adds popupMenuListener to listen for PopupMenuEvents fired
417 * @param listener PopupMenuListener to add to JPopupMenu
419 public void addPopupMenuListener(PopupMenuListener listener
)
421 listenerList
.add(PopupMenuListener
.class, listener
);
425 * Removes PopupMenuListener from JPopupMenu's list of listeners
427 * @param listener PopupMenuListener which needs to be removed
429 public void removePopupMenuListener(PopupMenuListener listener
)
431 listenerList
.remove(PopupMenuListener
.class, listener
);
435 * Returns array of PopupMenuListeners that are listening to JPopupMenu
437 * @return Array of PopupMenuListeners that are listening to JPopupMenu
439 public PopupMenuListener
[] getPopupMenuListeners()
441 return ((PopupMenuListener
[]) listenerList
.getListeners(PopupMenuListener
.class));
445 * This method calls popupMenuWillBecomeVisible() of popup menu's
446 * PopupMenuListeners. This method is invoked just before popup menu
447 * will appear on the screen.
449 protected void firePopupMenuWillBecomeVisible()
451 EventListener
[] ll
= listenerList
.getListeners(PopupMenuListener
.class);
453 for (int i
= 0; i
< ll
.length
; i
++)
454 ((PopupMenuListener
) ll
[i
]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
458 * This method calls popupMenuWillBecomeInvisible() of popup
459 * menu's PopupMenuListeners. This method is invoked just before popup
460 * menu will disappear from the screen
462 protected void firePopupMenuWillBecomeInvisible()
464 EventListener
[] ll
= listenerList
.getListeners(PopupMenuListener
.class);
466 for (int i
= 0; i
< ll
.length
; i
++)
467 ((PopupMenuListener
) ll
[i
]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
471 * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
472 * This method is invoked just before popup menu is cancelled. This happens
473 * when popup menu is closed without selecting any of its menu items. This
474 * usually happens when the top-level window is resized or moved.
476 protected void firePopupMenuCanceled()
478 EventListener
[] ll
= listenerList
.getListeners(PopupMenuListener
.class);
480 for (int i
= 0; i
< ll
.length
; i
++)
481 ((PopupMenuListener
) ll
[i
]).popupMenuCanceled(new PopupMenuEvent(this));
485 * This methods sets popup menu's size to its' preferred size. If the
486 * popup menu's size is previously set it will be ignored.
490 // Hook up this call so that it gets executed on the event thread in order
491 // to avoid synchronization problems when calling the layout manager.
492 if (! SwingUtilities
.isEventDispatchThread())
494 SwingUtilities
.invokeLater(new Runnable()
503 setSize(getPreferredSize());
507 * Return visibility of the popup menu
509 * @return true if popup menu is visible on the screen and false otherwise.
511 public boolean isVisible()
517 * Sets visibility property of this popup menu. If the property is
518 * set to true then popup menu will be dispayed and popup menu will
519 * hide itself if visible property is set to false.
521 * @param visible true if popup menu will become visible and false otherwise.
523 public void setVisible(final boolean visible
)
525 // Hook up this call so that it gets executed on the event thread in order
526 // to avoid synchronization problems when calling the layout manager.
527 if (! SwingUtilities
.isEventDispatchThread())
529 SwingUtilities
.invokeLater(new Runnable()
538 if (visible
== isVisible())
541 boolean old
= isVisible();
542 this.visible
= visible
;
543 if (old
!= isVisible())
545 firePropertyChange("visible", old
, isVisible());
548 firePopupMenuWillBecomeVisible();
550 PopupFactory pf
= PopupFactory
.getSharedInstance();
552 popup
= pf
.getPopup(invoker
, this, popupLocationX
, popupLocationY
);
557 firePopupMenuWillBecomeInvisible();
564 * Sets location of the popup menu.
566 * @param x X coordinate of the popup menu's location
567 * @param y Y coordinate of the popup menu's location
569 public void setLocation(int x
, int y
)
573 // Handle the case when the popup is already showing. In this case we need
574 // to fetch a new popup from PopupFactory and use this. See the general
575 // contract of the PopupFactory.
579 * Returns popup menu's invoker.
581 * @return popup menu's invoker
583 public Component
getInvoker()
589 * Sets popup menu's invoker.
591 * @param component The new invoker of this popup menu
593 public void setInvoker(Component component
)
599 * This method displays JPopupMenu on the screen at the specified
600 * location. Note that x and y coordinates given to this method
601 * should be expressed in terms of the popup menus' invoker.
603 * @param component Invoker for this popup menu
604 * @param x x-coordinate of the popup menu relative to the specified invoker
605 * @param y y-coordiate of the popup menu relative to the specified invoker
607 public void show(Component component
, int x
, int y
)
609 if (component
.isShowing())
611 setInvoker(component
);
612 Point p
= new Point(x
, y
);
613 SwingUtilities
.convertPointToScreen(p
, component
);
614 setLocation(p
.x
, p
.y
);
620 * Returns component located at the specified index in the popup menu
622 * @param index index of the component to return
624 * @return component located at the specified index in the popup menu
626 * @deprecated Replaced by getComponent(int)
628 public Component
getComponentAtIndex(int index
)
630 return getComponent(index
);
634 * Returns index of the specified component in the popup menu
636 * @param component Component to look for
638 * @return index of the specified component in the popup menu
640 public int getComponentIndex(Component component
)
642 Component
[] items
= getComponents();
644 for (int i
= 0; i
< items
.length
; i
++)
646 if (items
[i
].equals(component
))
654 * Sets size of the popup
656 * @param size Dimensions representing new size of the popup menu
658 public void setPopupSize(Dimension size
)
664 * Sets size of the popup menu
666 * @param width width for the new size
667 * @param height height for the new size
669 public void setPopupSize(int width
, int height
)
671 super.setSize(width
, height
);
675 * Selects specified component in this popup menu.
677 * @param selected component to select
679 public void setSelected(Component selected
)
681 int index
= getComponentIndex(selected
);
682 selectionModel
.setSelectedIndex(index
);
686 * Checks if this popup menu paints its border.
688 * @return true if this popup menu paints its border and false otherwise.
690 public boolean isBorderPainted()
692 return borderPainted
;
696 * Sets if the border of the popup menu should be
699 * @param painted true if the border should be painted and false otherwise
701 public void setBorderPainted(boolean painted
)
703 borderPainted
= painted
;
707 * Returns margin for this popup menu.
709 * @return margin for this popup menu.
711 public Insets
getMargin()
717 * A string that describes this JPopupMenu. Normally only used
720 * @return A string describing this JMenuItem
722 protected String
paramString()
724 StringBuffer sb
= new StringBuffer();
725 sb
.append(super.paramString());
726 sb
.append(",label=");
727 if (getLabel() != null)
728 sb
.append(getLabel());
729 sb
.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
730 sb
.append(",margin=");
731 if (getMargin() != null)
733 sb
.append(",paintBorder=").append(isBorderPainted());
734 return sb
.toString();
738 * Process mouse events forwarded from MenuSelectionManager. This method
739 * doesn't do anything. It is here to conform to the MenuElement interface.
741 * @param event event forwarded from MenuSelectionManager
742 * @param path path to the menu element from which event was generated
743 * @param manager MenuSelectionManager for the current menu hierarchy
745 public void processMouseEvent(MouseEvent event
, MenuElement
[] path
,
746 MenuSelectionManager manager
)
748 // Empty Implementation. This method is needed for the implementation
749 // of MenuElement interface
753 * Process key events forwarded from MenuSelectionManager. This method
754 * doesn't do anything. It is here to conform to the MenuElement interface.
756 * @param event event forwarded from MenuSelectionManager
757 * @param path path to the menu element from which event was generated
758 * @param manager MenuSelectionManager for the current menu hierarchy
761 public void processKeyEvent(KeyEvent event
, MenuElement
[] path
,
762 MenuSelectionManager manager
)
764 // Empty Implementation. This method is needed for the implementation
765 // of MenuElement interface
769 * Method of MenuElement Interface. It is invoked when
770 * popupMenu's selection has changed
772 * @param changed true if this popupMenu is part of current menu
773 * hierarchy and false otherwise.
775 public void menuSelectionChanged(boolean changed
)
782 * Return subcomonents of this popup menu. This method returns only
783 * components that implement the <code>MenuElement</code> interface.
785 * @return array of menu items belonging to this popup menu
787 public MenuElement
[] getSubElements()
789 Component
[] items
= getComponents();
790 ArrayList subElements
= new ArrayList();
792 for (int i
= 0; i
< items
.length
; i
++)
793 if (items
[i
] instanceof MenuElement
)
794 subElements
.add(items
[i
]);
796 return (MenuElement
[])
797 subElements
.toArray(new MenuElement
[subElements
.size()]);
801 * Method of the MenuElement interface. Returns reference to itself.
803 * @return Returns reference to itself
805 public Component
getComponent()
811 * Checks if observing mouse event should trigger popup
812 * menu to show on the screen.
814 * @param event MouseEvent to check
816 * @return true if the observing mouse event is popup trigger and false otherwise
818 public boolean isPopupTrigger(MouseEvent event
)
820 return ((PopupMenuUI
) getUI()).isPopupTrigger(event
);
826 * @return DOCUMENT ME!
828 public AccessibleContext
getAccessibleContext()
830 if (accessibleContext
== null)
831 accessibleContext
= new AccessibleJPopupMenu();
833 return accessibleContext
;
837 * This is the separator that can be used in popup menu.
839 public static class Separator
extends JSeparator
846 public String
getUIClassID()
848 return "PopupMenuSeparatorUI";
852 protected class AccessibleJPopupMenu
extends AccessibleJComponent
854 private static final long serialVersionUID
= 7423261328879849768L;
856 protected AccessibleJPopupMenu()
858 // Nothing to do here.
861 public AccessibleRole
getAccessibleRole()
863 return AccessibleRole
.POPUP_MENU
;
867 /* This class resizes popup menu and repaints popup menu appropriately if one
868 of item's action has changed */
869 private class ActionChangeListener
implements PropertyChangeListener
871 public void propertyChange(PropertyChangeEvent evt
)
873 // We used to have a revalidate() and repaint() call here. However I think
874 // this is not needed. Instead, a new Popup has to be fetched from the
875 // PopupFactory and used here.