1 /*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.swing;
27
28 import java.awt.AWTEvent;
29 import java.awt.Component;
30 import java.awt.ComponentOrientation;
31 import java.awt.Container;
32 import java.awt.Dimension;
33 import java.awt.Frame;
34 import java.awt.Graphics;
35 import java.awt.GraphicsConfiguration;
36 import java.awt.GraphicsDevice;
37 import java.awt.GraphicsEnvironment;
38 import java.awt.Insets;
39 import java.awt.Point;
40 import java.awt.Polygon;
41 import java.awt.Rectangle;
42 import java.awt.Toolkit;
43 import java.awt.event.*;
44 import java.beans.*;
45
46 import java.util.*;
47
48 import java.io.Serializable;
49 import java.io.ObjectOutputStream;
50 import java.io.ObjectInputStream;
51 import java.io.IOException;
52
53 import javax.swing.event.*;
54 import javax.swing.plaf.*;
55 import javax.swing.plaf.basic.*;
56 import javax.accessibility.*;
57
58 import java.lang.ref.WeakReference;
59
60 /**
61 * An implementation of a menu -- a popup window containing
62 * <code>JMenuItem</code>s that
63 * is displayed when the user selects an item on the <code>JMenuBar</code>.
64 * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can
65 * also contain <code>JSeparator</code>s.
66 * <p>
67 * In essence, a menu is a button with an associated <code>JPopupMenu</code>.
68 * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the
69 * "button" is on the <code>JMenuBar</code>, the menu is a top-level window.
70 * If the "button" is another menu item, then the <code>JPopupMenu</code> is
71 * "pull-right" menu.
72 * <p>
73 * Menus can be configured, and to some degree controlled, by
74 * <code><a href="Action.html">Action</a></code>s. Using an
75 * <code>Action</code> with a menu has many benefits beyond directly
76 * configuring a menu. Refer to <a href="Action.html#buttonActions">
77 * Swing Components Supporting <code>Action</code></a> for more
78 * details, and you can find more information in <a
79 * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How
80 * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
81 * <p>
82 * For information and examples of using menus see
83 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
84 * a section in <em>The Java Tutorial.</em>
85 * <p>
86 * <strong>Warning:</strong> Swing is not thread safe. For more
87 * information see <a
88 * href="package-summary.html#threading">Swing's Threading
89 * Policy</a>.
90 * <p>
91 * <strong>Warning:</strong>
92 * Serialized objects of this class will not be compatible with
93 * future Swing releases. The current serialization support is
94 * appropriate for short term storage or RMI between applications running
95 * the same version of Swing. As of 1.4, support for long term storage
96 * of all JavaBeans™
97 * has been added to the <code>java.beans</code> package.
98 * Please see {@link java.beans.XMLEncoder}.
99 *
100 * @beaninfo
101 * attribute: isContainer true
102 * description: A popup window containing menu items displayed in a menu bar.
103 *
104 * @author Georges Saab
105 * @author David Karlton
106 * @author Arnaud Weber
107 * @see JMenuItem
108 * @see JSeparator
109 * @see JMenuBar
110 * @see JPopupMenu
111 * @since 1.2
112 */
113 @SuppressWarnings("serial")
114 public class JMenu extends JMenuItem implements Accessible,MenuElement
115 {
116 /**
117 * @see #getUIClassID
118 * @see #readObject
119 */
120 private static final String uiClassID = "MenuUI";
121
122 /*
123 * The popup menu portion of the menu.
124 */
125 private JPopupMenu popupMenu;
126
127 /*
128 * The button's model listeners. Default is <code>null</code>.
129 */
130 private ChangeListener menuChangeListener = null;
131
132 /*
215 * @see JComponent#updateUI
216 */
217 public void updateUI() {
218 setUI((MenuItemUI)UIManager.getUI(this));
219
220 if ( popupMenu != null )
221 {
222 popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu));
223 }
224
225 }
226
227
228 /**
229 * Returns the name of the L&F class that renders this component.
230 *
231 * @return the string "MenuUI"
232 * @see JComponent#getUIClassID
233 * @see UIDefaults#getUI
234 */
235 public String getUIClassID() {
236 return uiClassID;
237 }
238
239 // public void repaint(long tm, int x, int y, int width, int height) {
240 // Thread.currentThread().dumpStack();
241 // super.repaint(tm,x,y,width,height);
242 // }
243
244 /**
245 * Sets the data model for the "menu button" -- the label
246 * that the user clicks to open or close the menu.
247 *
248 * @param newModel the <code>ButtonModel</code>
249 * @see #getModel
250 * @beaninfo
251 * description: The menu's model
252 * bound: true
253 * expert: true
254 * hidden: true
255 */
256 public void setModel(ButtonModel newModel) {
257 ButtonModel oldModel = getModel();
258
259 super.setModel(newModel);
260
261 if (oldModel != null && menuChangeListener != null) {
262 oldModel.removeChangeListener(menuChangeListener);
263 menuChangeListener = null;
264 }
265
266 model = newModel;
267
268 if (newModel != null) {
269 menuChangeListener = createMenuChangeListener();
270 newModel.addChangeListener(menuChangeListener);
271 }
272 }
273
274 /**
275 * Returns true if the menu is currently selected (highlighted).
276 *
277 * @return true if the menu is selected, else false
278 */
279 public boolean isSelected() {
280 return getModel().isSelected();
281 }
282
283 /**
284 * Sets the selection status of the menu.
285 *
286 * @param b true to select (highlight) the menu; false to de-select
287 * the menu
288 * @beaninfo
289 * description: When the menu is selected, its popup child is shown.
290 * expert: true
291 * hidden: true
292 */
293 public void setSelected(boolean b) {
294 ButtonModel model = getModel();
295 boolean oldValue = model.isSelected();
296
297 // TIGER - 4840653
298 // Removed code which fired an AccessibleState.SELECTED
299 // PropertyChangeEvent since this resulted in two
300 // identical events being fired since
301 // AbstractButton.fireItemStateChanged also fires the
302 // same event. This caused screen readers to speak the
303 // name of the item twice.
304
305 if (b != model.isSelected()) {
306 getModel().setSelected(b);
307 }
308 }
309
310 /**
311 * Returns true if the menu's popup window is visible.
312 *
313 * @return true if the menu is visible, else false
314 */
315 public boolean isPopupMenuVisible() {
316 ensurePopupMenuCreated();
317 return popupMenu.isVisible();
318 }
319
320 /**
321 * Sets the visibility of the menu's popup. If the menu is
322 * not enabled, this method will have no effect.
323 *
324 * @param b a boolean value -- true to make the menu visible,
325 * false to hide it
326 * @beaninfo
327 * description: The popup menu's visibility
328 * expert: true
329 * hidden: true
330 */
331 public void setPopupMenuVisible(boolean b) {
332 if (DEBUG) {
333 System.out.println("in JMenu.setPopupMenuVisible " + b);
334 // Thread.dumpStack();
335 }
336
337 boolean isVisible = isPopupMenuVisible();
338 if (b != isVisible && (isEnabled() || !b)) {
339 ensurePopupMenuCreated();
340 if ((b==true) && isShowing()) {
341 // Set location of popupMenu (pulldown or pullright)
342 Point p = getCustomMenuLocation();
343 if (p == null) {
344 p = getPopupMenuOrigin();
345 }
346 getPopupMenu().show(this, p.x, p.y);
347 } else {
348 getPopupMenu().setVisible(false);
349 }
350 }
500 * to manage the idiosyncrasies of the various UI implementations.
501 *
502 *
503 * @return the <code>delay</code> property
504 */
505 public int getDelay() {
506 return delay;
507 }
508
509 /**
510 * Sets the suggested delay before the menu's <code>PopupMenu</code>
511 * is popped up or down. Each look and feel (L&F) may determine
512 * it's own policy for observing the delay property. In most cases,
513 * the delay is not observed for top level menus or while dragging.
514 * This method is a property of the look and feel code and is used
515 * to manage the idiosyncrasies of the various UI implementations.
516 *
517 * @param d the number of milliseconds to delay
518 * @exception IllegalArgumentException if <code>d</code>
519 * is less than 0
520 * @beaninfo
521 * description: The delay between menu selection and making the popup menu visible
522 * expert: true
523 */
524 public void setDelay(int d) {
525 if (d < 0)
526 throw new IllegalArgumentException("Delay must be a positive integer");
527
528 delay = d;
529 }
530
531 /**
532 * The window-closing listener for the popup.
533 *
534 * @see WinListener
535 */
536 protected WinListener popupListener;
537
538 private void ensurePopupMenuCreated() {
539 if (popupMenu == null) {
540 final JMenu thisMenu = this;
541 this.popupMenu = new JPopupMenu();
542 popupMenu.setInvoker(this);
543 popupListener = createWinListener(popupMenu);
771 throw new IllegalArgumentException("index less than zero.");
772 }
773
774 Component c = getMenuComponent(pos);
775 if (c instanceof JMenuItem) {
776 JMenuItem mi = (JMenuItem) c;
777 return mi;
778 }
779
780 // 4173633
781 return null;
782 }
783
784 /**
785 * Returns the number of items on the menu, including separators.
786 * This method is included for AWT compatibility.
787 *
788 * @return an integer equal to the number of items on the menu
789 * @see #getMenuComponentCount
790 */
791 public int getItemCount() {
792 return getMenuComponentCount();
793 }
794
795 /**
796 * Returns true if the menu can be torn off. This method is not
797 * yet implemented.
798 *
799 * @return true if the menu can be torn off, else false
800 * @exception Error if invoked -- this method is not yet implemented
801 */
802 public boolean isTearOff() {
803 throw new Error("boolean isTearOff() {} not yet implemented");
804 }
805
806 /**
807 * Removes the specified menu item from this menu. If there is no
808 * popup menu, this method will have no effect.
809 *
810 * @param item the <code>JMenuItem</code> to be removed from the menu
811 */
812 public void remove(JMenuItem item) {
813 if (popupMenu != null)
814 popupMenu.remove(item);
815 }
816
817 /**
818 * Removes the menu item at the specified index from this menu.
819 *
820 * @param pos the position of the item to be removed
821 * @exception IllegalArgumentException if the value of
839 * @param c the component to be removed
840 */
841 public void remove(Component c) {
842 if (popupMenu != null)
843 popupMenu.remove(c);
844 }
845
846 /**
847 * Removes all menu items from this menu.
848 */
849 public void removeAll() {
850 if (popupMenu != null)
851 popupMenu.removeAll();
852 }
853
854 /**
855 * Returns the number of components on the menu.
856 *
857 * @return an integer containing the number of components on the menu
858 */
859 public int getMenuComponentCount() {
860 int componentCount = 0;
861 if (popupMenu != null)
862 componentCount = popupMenu.getComponentCount();
863 return componentCount;
864 }
865
866 /**
867 * Returns the component at position <code>n</code>.
868 *
869 * @param n the position of the component to be returned
870 * @return the component requested, or <code>null</code>
871 * if there is no popup menu
872 *
873 */
874 public Component getMenuComponent(int n) {
875 if (popupMenu != null)
876 return popupMenu.getComponent(n);
877
878 return null;
879 }
880
881 /**
882 * Returns an array of <code>Component</code>s of the menu's
883 * subcomponents. Note that this returns all <code>Component</code>s
884 * in the popup menu, including separators.
885 *
886 * @return an array of <code>Component</code>s or an empty array
887 * if there is no popup menu
888 */
889 public Component[] getMenuComponents() {
890 if (popupMenu != null)
891 return popupMenu.getComponents();
892
893 return new Component[0];
894 }
895
896 /**
897 * Returns true if the menu is a 'top-level menu', that is, if it is
898 * the direct child of a menubar.
899 *
900 * @return true if the menu is activated from the menu bar;
901 * false if the menu is activated from a menu item
902 * on another menu
903 */
904 public boolean isTopLevelMenu() {
905 return getParent() instanceof JMenuBar;
906
907 }
908
909 /**
910 * Returns true if the specified component exists in the
911 * submenu hierarchy.
912 *
913 * @param c the <code>Component</code> to be tested
914 * @return true if the <code>Component</code> exists, false otherwise
915 */
916 public boolean isMenuComponent(Component c) {
917 // Are we in the MenuItem part of the menu
918 if (c == this)
919 return true;
920 // Are we in the PopupMenu?
921 if (c instanceof JPopupMenu) {
922 JPopupMenu comp = (JPopupMenu) c;
923 if (comp == this.getPopupMenu())
968 int newX;
969 int newY;
970
971 if (getParent() instanceof JPopupMenu) {
972 newX = x - getSize().width;
973 newY = y;
974 } else {
975 newX = x;
976 newY = y - getSize().height;
977 }
978
979 return new Point(newX, newY);
980 }
981
982 /**
983 * Returns the popupmenu associated with this menu. If there is
984 * no popupmenu, it will create one.
985 *
986 * @return the {@code JPopupMenu} associated with this menu
987 */
988 public JPopupMenu getPopupMenu() {
989 ensurePopupMenuCreated();
990 return popupMenu;
991 }
992
993 /**
994 * Adds a listener for menu events.
995 *
996 * @param l the listener to be added
997 */
998 public void addMenuListener(MenuListener l) {
999 listenerList.add(MenuListener.class, l);
1000 }
1001
1002 /**
1003 * Removes a listener for menu events.
1004 *
1005 * @param l the listener to be removed
1006 */
1007 public void removeMenuListener(MenuListener l) {
1008 listenerList.remove(MenuListener.class, l);
1009 }
1010
1011 /**
1012 * Returns an array of all the <code>MenuListener</code>s added
1013 * to this JMenu with addMenuListener().
1014 *
1015 * @return all of the <code>MenuListener</code>s added or an empty
1016 * array if no listeners have been added
1017 * @since 1.4
1018 */
1019 public MenuListener[] getMenuListeners() {
1020 return listenerList.getListeners(MenuListener.class);
1021 }
1022
1023 /**
1024 * Notifies all listeners that have registered interest for
1025 * notification on this event type. The event instance
1026 * is created lazily.
1027 *
1028 * @exception Error if there is a <code>null</code> listener
1029 * @see EventListenerList
1030 */
1031 protected void fireMenuSelected() {
1032 if (DEBUG) {
1033 System.out.println("In JMenu.fireMenuSelected");
1034 }
1035 // Guaranteed to return a non-null array
1036 Object[] listeners = listenerList.getListenerList();
1037 // Process the listeners last to first, notifying
1038 // those that are interested in this event
1191 * @param isIncluded true if this menu is active, false if
1192 * it is not
1193 */
1194 public void menuSelectionChanged(boolean isIncluded) {
1195 if (DEBUG) {
1196 System.out.println("In JMenu.menuSelectionChanged to " + isIncluded);
1197 }
1198 setSelected(isIncluded);
1199 }
1200
1201 /**
1202 * Returns an array of <code>MenuElement</code>s containing the submenu
1203 * for this menu component. If popup menu is <code>null</code> returns
1204 * an empty array. This method is required to conform to the
1205 * <code>MenuElement</code> interface. Note that since
1206 * <code>JSeparator</code>s do not conform to the <code>MenuElement</code>
1207 * interface, this array will only contain <code>JMenuItem</code>s.
1208 *
1209 * @return an array of <code>MenuElement</code> objects
1210 */
1211 public MenuElement[] getSubElements() {
1212 if(popupMenu == null)
1213 return new MenuElement[0];
1214 else {
1215 MenuElement result[] = new MenuElement[1];
1216 result[0] = popupMenu;
1217 return result;
1218 }
1219 }
1220
1221
1222 // implements javax.swing.MenuElement
1223 /**
1224 * Returns the <code>java.awt.Component</code> used to
1225 * paint this <code>MenuElement</code>.
1226 * The returned component is used to convert events and detect if
1227 * an event is inside a menu component.
1228 */
1229 public Component getComponent() {
1230 return this;
1253 }
1254 popupMenu.setComponentOrientation(o);
1255 }
1256 }
1257
1258 public void setComponentOrientation(ComponentOrientation o) {
1259 super.setComponentOrientation(o);
1260 if ( popupMenu != null ) {
1261 popupMenu.setComponentOrientation(o);
1262 }
1263 }
1264
1265 /**
1266 * <code>setAccelerator</code> is not defined for <code>JMenu</code>.
1267 * Use <code>setMnemonic</code> instead.
1268 * @param keyStroke the keystroke combination which will invoke
1269 * the <code>JMenuItem</code>'s actionlisteners
1270 * without navigating the menu hierarchy
1271 * @exception Error if invoked -- this method is not defined for JMenu.
1272 * Use <code>setMnemonic</code> instead
1273 *
1274 * @beaninfo
1275 * description: The keystroke combination which will invoke the JMenuItem's
1276 * actionlisteners without navigating the menu hierarchy
1277 * hidden: true
1278 */
1279 public void setAccelerator(KeyStroke keyStroke) {
1280 throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
1281 }
1282
1283 /**
1284 * Processes key stroke events such as mnemonics and accelerators.
1285 *
1286 * @param evt the key event to be processed
1287 */
1288 protected void processKeyEvent(KeyEvent evt) {
1289 MenuSelectionManager.defaultManager().processKeyEvent(evt);
1290 if (evt.isConsumed())
1291 return;
1292
1293 super.processKeyEvent(evt);
1294 }
1295
1296 /**
1297 * Programmatically performs a "click". This overrides the method
1364 * @return a string representation of this JMenu.
1365 */
1366 protected String paramString() {
1367 return super.paramString();
1368 }
1369
1370
1371 /////////////////
1372 // Accessibility support
1373 ////////////////
1374
1375 /**
1376 * Gets the AccessibleContext associated with this JMenu.
1377 * For JMenus, the AccessibleContext takes the form of an
1378 * AccessibleJMenu.
1379 * A new AccessibleJMenu instance is created if necessary.
1380 *
1381 * @return an AccessibleJMenu that serves as the
1382 * AccessibleContext of this JMenu
1383 */
1384 public AccessibleContext getAccessibleContext() {
1385 if (accessibleContext == null) {
1386 accessibleContext = new AccessibleJMenu();
1387 }
1388 return accessibleContext;
1389 }
1390
1391 /**
1392 * This class implements accessibility support for the
1393 * <code>JMenu</code> class. It provides an implementation of the
1394 * Java Accessibility API appropriate to menu user-interface elements.
1395 * <p>
1396 * <strong>Warning:</strong>
1397 * Serialized objects of this class will not be compatible with
1398 * future Swing releases. The current serialization support is
1399 * appropriate for short term storage or RMI between applications running
1400 * the same version of Swing. As of 1.4, support for long term storage
1401 * of all JavaBeans™
1402 * has been added to the <code>java.beans</code> package.
1403 * Please see {@link java.beans.XMLEncoder}.
|
1 /*
2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package javax.swing;
26
27 import java.awt.Component;
28 import java.awt.ComponentOrientation;
29 import java.awt.Container;
30 import java.awt.Dimension;
31 import java.awt.GraphicsConfiguration;
32 import java.awt.GraphicsDevice;
33 import java.awt.GraphicsEnvironment;
34 import java.awt.Insets;
35 import java.awt.Point;
36 import java.awt.Rectangle;
37 import java.awt.Toolkit;
38 import java.awt.event.*;
39 import java.beans.JavaBean;
40 import java.beans.BeanProperty;
41 import java.beans.PropertyChangeListener;
42
43 import java.util.*;
44
45 import java.io.Serializable;
46 import java.io.ObjectOutputStream;
47 import java.io.IOException;
48
49 import javax.swing.event.*;
50 import javax.swing.plaf.*;
51 import javax.accessibility.*;
52
53 /**
54 * An implementation of a menu -- a popup window containing
55 * <code>JMenuItem</code>s that
56 * is displayed when the user selects an item on the <code>JMenuBar</code>.
57 * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can
58 * also contain <code>JSeparator</code>s.
59 * <p>
60 * In essence, a menu is a button with an associated <code>JPopupMenu</code>.
61 * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the
62 * "button" is on the <code>JMenuBar</code>, the menu is a top-level window.
63 * If the "button" is another menu item, then the <code>JPopupMenu</code> is
64 * "pull-right" menu.
65 * <p>
66 * Menus can be configured, and to some degree controlled, by
67 * <code><a href="Action.html">Action</a></code>s. Using an
68 * <code>Action</code> with a menu has many benefits beyond directly
69 * configuring a menu. Refer to <a href="Action.html#buttonActions">
70 * Swing Components Supporting <code>Action</code></a> for more
71 * details, and you can find more information in <a
72 * href="http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html">How
73 * to Use Actions</a>, a section in <em>The Java Tutorial</em>.
74 * <p>
75 * For information and examples of using menus see
76 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
77 * a section in <em>The Java Tutorial.</em>
78 * <p>
79 * <strong>Warning:</strong> Swing is not thread safe. For more
80 * information see <a
81 * href="package-summary.html#threading">Swing's Threading
82 * Policy</a>.
83 * <p>
84 * <strong>Warning:</strong>
85 * Serialized objects of this class will not be compatible with
86 * future Swing releases. The current serialization support is
87 * appropriate for short term storage or RMI between applications running
88 * the same version of Swing. As of 1.4, support for long term storage
89 * of all JavaBeans™
90 * has been added to the <code>java.beans</code> package.
91 * Please see {@link java.beans.XMLEncoder}.
92 *
93 * @author Georges Saab
94 * @author David Karlton
95 * @author Arnaud Weber
96 * @see JMenuItem
97 * @see JSeparator
98 * @see JMenuBar
99 * @see JPopupMenu
100 * @since 1.2
101 */
102 @JavaBean(description = "A popup window containing menu items displayed in a menu bar.")
103 @SwingContainer
104 @SuppressWarnings("serial")
105 public class JMenu extends JMenuItem implements Accessible,MenuElement
106 {
107 /**
108 * @see #getUIClassID
109 * @see #readObject
110 */
111 private static final String uiClassID = "MenuUI";
112
113 /*
114 * The popup menu portion of the menu.
115 */
116 private JPopupMenu popupMenu;
117
118 /*
119 * The button's model listeners. Default is <code>null</code>.
120 */
121 private ChangeListener menuChangeListener = null;
122
123 /*
206 * @see JComponent#updateUI
207 */
208 public void updateUI() {
209 setUI((MenuItemUI)UIManager.getUI(this));
210
211 if ( popupMenu != null )
212 {
213 popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu));
214 }
215
216 }
217
218
219 /**
220 * Returns the name of the L&F class that renders this component.
221 *
222 * @return the string "MenuUI"
223 * @see JComponent#getUIClassID
224 * @see UIDefaults#getUI
225 */
226 @BeanProperty(bound = false)
227 public String getUIClassID() {
228 return uiClassID;
229 }
230
231 // public void repaint(long tm, int x, int y, int width, int height) {
232 // Thread.currentThread().dumpStack();
233 // super.repaint(tm,x,y,width,height);
234 // }
235
236 /**
237 * Sets the data model for the "menu button" -- the label
238 * that the user clicks to open or close the menu.
239 *
240 * @param newModel the <code>ButtonModel</code>
241 * @see #getModel
242 */
243 public void setModel(ButtonModel newModel) {
244 ButtonModel oldModel = getModel();
245
246 super.setModel(newModel);
247
248 if (oldModel != null && menuChangeListener != null) {
249 oldModel.removeChangeListener(menuChangeListener);
250 menuChangeListener = null;
251 }
252
253 model = newModel;
254
255 if (newModel != null) {
256 menuChangeListener = createMenuChangeListener();
257 newModel.addChangeListener(menuChangeListener);
258 }
259 }
260
261 /**
262 * Returns true if the menu is currently selected (highlighted).
263 *
264 * @return true if the menu is selected, else false
265 */
266 public boolean isSelected() {
267 return getModel().isSelected();
268 }
269
270 /**
271 * Sets the selection status of the menu.
272 *
273 * @param b true to select (highlight) the menu; false to de-select
274 * the menu
275 */
276 @BeanProperty(expert = true, hidden = true, description
277 = "When the menu is selected, its popup child is shown.")
278 public void setSelected(boolean b) {
279 ButtonModel model = getModel();
280 boolean oldValue = model.isSelected();
281
282 // TIGER - 4840653
283 // Removed code which fired an AccessibleState.SELECTED
284 // PropertyChangeEvent since this resulted in two
285 // identical events being fired since
286 // AbstractButton.fireItemStateChanged also fires the
287 // same event. This caused screen readers to speak the
288 // name of the item twice.
289
290 if (b != model.isSelected()) {
291 getModel().setSelected(b);
292 }
293 }
294
295 /**
296 * Returns true if the menu's popup window is visible.
297 *
298 * @return true if the menu is visible, else false
299 */
300 public boolean isPopupMenuVisible() {
301 ensurePopupMenuCreated();
302 return popupMenu.isVisible();
303 }
304
305 /**
306 * Sets the visibility of the menu's popup. If the menu is
307 * not enabled, this method will have no effect.
308 *
309 * @param b a boolean value -- true to make the menu visible,
310 * false to hide it
311 */
312 @BeanProperty(bound = false, expert = true, hidden = true, description
313 = "The popup menu's visibility")
314 public void setPopupMenuVisible(boolean b) {
315 if (DEBUG) {
316 System.out.println("in JMenu.setPopupMenuVisible " + b);
317 // Thread.dumpStack();
318 }
319
320 boolean isVisible = isPopupMenuVisible();
321 if (b != isVisible && (isEnabled() || !b)) {
322 ensurePopupMenuCreated();
323 if ((b==true) && isShowing()) {
324 // Set location of popupMenu (pulldown or pullright)
325 Point p = getCustomMenuLocation();
326 if (p == null) {
327 p = getPopupMenuOrigin();
328 }
329 getPopupMenu().show(this, p.x, p.y);
330 } else {
331 getPopupMenu().setVisible(false);
332 }
333 }
483 * to manage the idiosyncrasies of the various UI implementations.
484 *
485 *
486 * @return the <code>delay</code> property
487 */
488 public int getDelay() {
489 return delay;
490 }
491
492 /**
493 * Sets the suggested delay before the menu's <code>PopupMenu</code>
494 * is popped up or down. Each look and feel (L&F) may determine
495 * it's own policy for observing the delay property. In most cases,
496 * the delay is not observed for top level menus or while dragging.
497 * This method is a property of the look and feel code and is used
498 * to manage the idiosyncrasies of the various UI implementations.
499 *
500 * @param d the number of milliseconds to delay
501 * @exception IllegalArgumentException if <code>d</code>
502 * is less than 0
503 */
504 @BeanProperty(bound = false, expert = true, description
505 = "The delay between menu selection and making the popup menu visible")
506 public void setDelay(int d) {
507 if (d < 0)
508 throw new IllegalArgumentException("Delay must be a positive integer");
509
510 delay = d;
511 }
512
513 /**
514 * The window-closing listener for the popup.
515 *
516 * @see WinListener
517 */
518 protected WinListener popupListener;
519
520 private void ensurePopupMenuCreated() {
521 if (popupMenu == null) {
522 final JMenu thisMenu = this;
523 this.popupMenu = new JPopupMenu();
524 popupMenu.setInvoker(this);
525 popupListener = createWinListener(popupMenu);
753 throw new IllegalArgumentException("index less than zero.");
754 }
755
756 Component c = getMenuComponent(pos);
757 if (c instanceof JMenuItem) {
758 JMenuItem mi = (JMenuItem) c;
759 return mi;
760 }
761
762 // 4173633
763 return null;
764 }
765
766 /**
767 * Returns the number of items on the menu, including separators.
768 * This method is included for AWT compatibility.
769 *
770 * @return an integer equal to the number of items on the menu
771 * @see #getMenuComponentCount
772 */
773 @BeanProperty(bound = false)
774 public int getItemCount() {
775 return getMenuComponentCount();
776 }
777
778 /**
779 * Returns true if the menu can be torn off. This method is not
780 * yet implemented.
781 *
782 * @return true if the menu can be torn off, else false
783 * @exception Error if invoked -- this method is not yet implemented
784 */
785 @BeanProperty(bound = false)
786 public boolean isTearOff() {
787 throw new Error("boolean isTearOff() {} not yet implemented");
788 }
789
790 /**
791 * Removes the specified menu item from this menu. If there is no
792 * popup menu, this method will have no effect.
793 *
794 * @param item the <code>JMenuItem</code> to be removed from the menu
795 */
796 public void remove(JMenuItem item) {
797 if (popupMenu != null)
798 popupMenu.remove(item);
799 }
800
801 /**
802 * Removes the menu item at the specified index from this menu.
803 *
804 * @param pos the position of the item to be removed
805 * @exception IllegalArgumentException if the value of
823 * @param c the component to be removed
824 */
825 public void remove(Component c) {
826 if (popupMenu != null)
827 popupMenu.remove(c);
828 }
829
830 /**
831 * Removes all menu items from this menu.
832 */
833 public void removeAll() {
834 if (popupMenu != null)
835 popupMenu.removeAll();
836 }
837
838 /**
839 * Returns the number of components on the menu.
840 *
841 * @return an integer containing the number of components on the menu
842 */
843 @BeanProperty(bound = false)
844 public int getMenuComponentCount() {
845 int componentCount = 0;
846 if (popupMenu != null)
847 componentCount = popupMenu.getComponentCount();
848 return componentCount;
849 }
850
851 /**
852 * Returns the component at position <code>n</code>.
853 *
854 * @param n the position of the component to be returned
855 * @return the component requested, or <code>null</code>
856 * if there is no popup menu
857 *
858 */
859 public Component getMenuComponent(int n) {
860 if (popupMenu != null)
861 return popupMenu.getComponent(n);
862
863 return null;
864 }
865
866 /**
867 * Returns an array of <code>Component</code>s of the menu's
868 * subcomponents. Note that this returns all <code>Component</code>s
869 * in the popup menu, including separators.
870 *
871 * @return an array of <code>Component</code>s or an empty array
872 * if there is no popup menu
873 */
874 @BeanProperty(bound = false)
875 public Component[] getMenuComponents() {
876 if (popupMenu != null)
877 return popupMenu.getComponents();
878
879 return new Component[0];
880 }
881
882 /**
883 * Returns true if the menu is a 'top-level menu', that is, if it is
884 * the direct child of a menubar.
885 *
886 * @return true if the menu is activated from the menu bar;
887 * false if the menu is activated from a menu item
888 * on another menu
889 */
890 @BeanProperty(bound = false)
891 public boolean isTopLevelMenu() {
892 return getParent() instanceof JMenuBar;
893
894 }
895
896 /**
897 * Returns true if the specified component exists in the
898 * submenu hierarchy.
899 *
900 * @param c the <code>Component</code> to be tested
901 * @return true if the <code>Component</code> exists, false otherwise
902 */
903 public boolean isMenuComponent(Component c) {
904 // Are we in the MenuItem part of the menu
905 if (c == this)
906 return true;
907 // Are we in the PopupMenu?
908 if (c instanceof JPopupMenu) {
909 JPopupMenu comp = (JPopupMenu) c;
910 if (comp == this.getPopupMenu())
955 int newX;
956 int newY;
957
958 if (getParent() instanceof JPopupMenu) {
959 newX = x - getSize().width;
960 newY = y;
961 } else {
962 newX = x;
963 newY = y - getSize().height;
964 }
965
966 return new Point(newX, newY);
967 }
968
969 /**
970 * Returns the popupmenu associated with this menu. If there is
971 * no popupmenu, it will create one.
972 *
973 * @return the {@code JPopupMenu} associated with this menu
974 */
975 @BeanProperty(bound = false)
976 public JPopupMenu getPopupMenu() {
977 ensurePopupMenuCreated();
978 return popupMenu;
979 }
980
981 /**
982 * Adds a listener for menu events.
983 *
984 * @param l the listener to be added
985 */
986 public void addMenuListener(MenuListener l) {
987 listenerList.add(MenuListener.class, l);
988 }
989
990 /**
991 * Removes a listener for menu events.
992 *
993 * @param l the listener to be removed
994 */
995 public void removeMenuListener(MenuListener l) {
996 listenerList.remove(MenuListener.class, l);
997 }
998
999 /**
1000 * Returns an array of all the <code>MenuListener</code>s added
1001 * to this JMenu with addMenuListener().
1002 *
1003 * @return all of the <code>MenuListener</code>s added or an empty
1004 * array if no listeners have been added
1005 * @since 1.4
1006 */
1007 @BeanProperty(bound = false)
1008 public MenuListener[] getMenuListeners() {
1009 return listenerList.getListeners(MenuListener.class);
1010 }
1011
1012 /**
1013 * Notifies all listeners that have registered interest for
1014 * notification on this event type. The event instance
1015 * is created lazily.
1016 *
1017 * @exception Error if there is a <code>null</code> listener
1018 * @see EventListenerList
1019 */
1020 protected void fireMenuSelected() {
1021 if (DEBUG) {
1022 System.out.println("In JMenu.fireMenuSelected");
1023 }
1024 // Guaranteed to return a non-null array
1025 Object[] listeners = listenerList.getListenerList();
1026 // Process the listeners last to first, notifying
1027 // those that are interested in this event
1180 * @param isIncluded true if this menu is active, false if
1181 * it is not
1182 */
1183 public void menuSelectionChanged(boolean isIncluded) {
1184 if (DEBUG) {
1185 System.out.println("In JMenu.menuSelectionChanged to " + isIncluded);
1186 }
1187 setSelected(isIncluded);
1188 }
1189
1190 /**
1191 * Returns an array of <code>MenuElement</code>s containing the submenu
1192 * for this menu component. If popup menu is <code>null</code> returns
1193 * an empty array. This method is required to conform to the
1194 * <code>MenuElement</code> interface. Note that since
1195 * <code>JSeparator</code>s do not conform to the <code>MenuElement</code>
1196 * interface, this array will only contain <code>JMenuItem</code>s.
1197 *
1198 * @return an array of <code>MenuElement</code> objects
1199 */
1200 @BeanProperty(bound = false)
1201 public MenuElement[] getSubElements() {
1202 if(popupMenu == null)
1203 return new MenuElement[0];
1204 else {
1205 MenuElement result[] = new MenuElement[1];
1206 result[0] = popupMenu;
1207 return result;
1208 }
1209 }
1210
1211
1212 // implements javax.swing.MenuElement
1213 /**
1214 * Returns the <code>java.awt.Component</code> used to
1215 * paint this <code>MenuElement</code>.
1216 * The returned component is used to convert events and detect if
1217 * an event is inside a menu component.
1218 */
1219 public Component getComponent() {
1220 return this;
1243 }
1244 popupMenu.setComponentOrientation(o);
1245 }
1246 }
1247
1248 public void setComponentOrientation(ComponentOrientation o) {
1249 super.setComponentOrientation(o);
1250 if ( popupMenu != null ) {
1251 popupMenu.setComponentOrientation(o);
1252 }
1253 }
1254
1255 /**
1256 * <code>setAccelerator</code> is not defined for <code>JMenu</code>.
1257 * Use <code>setMnemonic</code> instead.
1258 * @param keyStroke the keystroke combination which will invoke
1259 * the <code>JMenuItem</code>'s actionlisteners
1260 * without navigating the menu hierarchy
1261 * @exception Error if invoked -- this method is not defined for JMenu.
1262 * Use <code>setMnemonic</code> instead
1263 */
1264 public void setAccelerator(KeyStroke keyStroke) {
1265 throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
1266 }
1267
1268 /**
1269 * Processes key stroke events such as mnemonics and accelerators.
1270 *
1271 * @param evt the key event to be processed
1272 */
1273 protected void processKeyEvent(KeyEvent evt) {
1274 MenuSelectionManager.defaultManager().processKeyEvent(evt);
1275 if (evt.isConsumed())
1276 return;
1277
1278 super.processKeyEvent(evt);
1279 }
1280
1281 /**
1282 * Programmatically performs a "click". This overrides the method
1349 * @return a string representation of this JMenu.
1350 */
1351 protected String paramString() {
1352 return super.paramString();
1353 }
1354
1355
1356 /////////////////
1357 // Accessibility support
1358 ////////////////
1359
1360 /**
1361 * Gets the AccessibleContext associated with this JMenu.
1362 * For JMenus, the AccessibleContext takes the form of an
1363 * AccessibleJMenu.
1364 * A new AccessibleJMenu instance is created if necessary.
1365 *
1366 * @return an AccessibleJMenu that serves as the
1367 * AccessibleContext of this JMenu
1368 */
1369 @BeanProperty(bound = false)
1370 public AccessibleContext getAccessibleContext() {
1371 if (accessibleContext == null) {
1372 accessibleContext = new AccessibleJMenu();
1373 }
1374 return accessibleContext;
1375 }
1376
1377 /**
1378 * This class implements accessibility support for the
1379 * <code>JMenu</code> class. It provides an implementation of the
1380 * Java Accessibility API appropriate to menu user-interface elements.
1381 * <p>
1382 * <strong>Warning:</strong>
1383 * Serialized objects of this class will not be compatible with
1384 * future Swing releases. The current serialization support is
1385 * appropriate for short term storage or RMI between applications running
1386 * the same version of Swing. As of 1.4, support for long term storage
1387 * of all JavaBeans™
1388 * has been added to the <code>java.beans</code> package.
1389 * Please see {@link java.beans.XMLEncoder}.
|