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 /*
133 * Only one <code>MenuEvent</code> is needed for each menu since the
134 * event's only state is the source property. The source of events
135 * generated is always "this". Default is <code>null</code>.
136 */
137 private MenuEvent menuEvent = null;
138
139 /*
140 * Used by the look and feel (L&F) code to handle
141 * implementation specific menu behaviors.
142 */
143 private int delay;
144
145 /*
146 * Location of the popup component. Location is <code>null</code>
147 * if it was not customized by <code>setMenuLocation</code>
148 */
149 private Point customMenuLocation = null;
150
151 /* Diagnostic aids -- should be false for production builds. */
152 private static final boolean TRACE = false; // trace creates and disposes
153 private static final boolean VERBOSE = false; // show reuse hits/misses
154 private static final boolean DEBUG = false; // show bad params, misc.
155
156 /**
157 * Constructs a new <code>JMenu</code> with no text.
158 */
159 public JMenu() {
160 this("");
161 }
162
163 /**
164 * Constructs a new <code>JMenu</code> with the supplied string
165 * as its text.
166 *
167 * @param s the text for the menu label
168 */
169 public JMenu(String s) {
170 super(s);
171 }
172
173 /**
174 * Constructs a menu whose properties are taken from the
175 * <code>Action</code> supplied.
176 * @param a an <code>Action</code>
177 *
178 * @since 1.3
179 */
180 public JMenu(Action a) {
181 this();
182 setAction(a);
183 }
184
185 /**
186 * Constructs a new <code>JMenu</code> with the supplied string as
187 * its text and specified as a tear-off menu or not.
188 *
189 * @param s the text for the menu label
190 * @param b can the menu be torn off (not yet implemented)
191 */
192 public JMenu(String s, boolean b) {
193 this(s);
194 }
195
196
197 /**
198 * Overriden to do nothing. We want JMenu to be focusable, but
199 * <code>JMenuItem</code> doesn't want to be, thus we override this
200 * do nothing. We don't invoke <code>setFocusable(true)</code> after
201 * super's constructor has completed as this has the side effect that
202 * <code>JMenu</code> will be considered traversable via the
203 * keyboard, which we don't want. Making a Component traversable by
204 * the keyboard after invoking <code>setFocusable(true)</code> is OK,
205 * as <code>setFocusable</code> is new API
206 * and is speced as such, but internally we don't want to use it like
207 * this else we change the keyboard traversability.
208 */
209 void initFocusability() {
210 }
211
212 /**
213 * Resets the UI property with a value from the current look and feel.
214 *
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 }
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) {
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 }
351 }
352
353 /**
354 * Computes the origin for the <code>JMenu</code>'s popup menu.
355 * This method uses Look and Feel properties named
356 * <code>Menu.menuPopupOffsetX</code>,
357 * <code>Menu.menuPopupOffsetY</code>,
358 * <code>Menu.submenuPopupOffsetX</code>, and
359 * <code>Menu.submenuPopupOffsetY</code>
360 * to adjust the exact location of popup.
361 *
362 * @return a <code>Point</code> in the coordinate space of the
363 * menu which should be used as the origin
364 * of the <code>JMenu</code>'s popup menu
365 *
366 * @since 1.3
367 */
368 protected Point getPopupMenuOrigin() {
369 int x;
370 int y;
371 JPopupMenu pm = getPopupMenu();
372 // Figure out the sizes needed to caclulate the menu position
373 Dimension s = getSize();
374 Dimension pmSize = pm.getSize();
375 // For the first time the menu is popped up,
376 // the size has not yet been initiated
377 if (pmSize.width==0) {
378 pmSize = pm.getPreferredSize();
379 }
380 Point position = getLocationOnScreen();
381 Toolkit toolkit = Toolkit.getDefaultToolkit();
382 GraphicsConfiguration gc = getGraphicsConfiguration();
383 Rectangle screenBounds = new Rectangle(toolkit.getScreenSize());
384 GraphicsEnvironment ge =
476 }
477 // Then the y:
478 y = s.height + yOffset; // Prefer dropping down
479 if (position.y + y + pmSize.height >= screenBounds.height
480 + screenBounds.y &&
481 // popup doesn't fit - place it wherever there's more room
482 screenBounds.height - s.height < 2*(position.y
483 - screenBounds.y)) {
484
485 y = 0 - yOffset - pmSize.height; // Otherwise drop 'up'
486 }
487 }
488 return new Point(x,y);
489 }
490
491
492 /**
493 * Returns the suggested delay, in milliseconds, before submenus
494 * are popped up or down.
495 * Each look and feel (L&F) may determine its own policy for
496 * observing the <code>delay</code> property.
497 * In most cases, the delay is not observed for top level menus
498 * or while dragging. The default for <code>delay</code> is 0.
499 * This method is a property of the look and feel code and is used
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() {
550 private Point getCustomMenuLocation() {
551 return customMenuLocation;
552 }
553
554 /**
555 * Sets the location of the popup component.
556 *
557 * @param x the x coordinate of the popup's new position
558 * @param y the y coordinate of the popup's new position
559 */
560 public void setMenuLocation(int x, int y) {
561 customMenuLocation = new Point(x, y);
562 if (popupMenu != null)
563 popupMenu.setLocation(x, y);
564 }
565
566 /**
567 * Appends a menu item to the end of this menu.
568 * Returns the menu item added.
569 *
570 * @param menuItem the <code>JMenuitem</code> to be added
571 * @return the <code>JMenuItem</code> added
572 */
573 public JMenuItem add(JMenuItem menuItem) {
574 ensurePopupMenuCreated();
575 return popupMenu.add(menuItem);
576 }
577
578 /**
579 * Appends a component to the end of this menu.
580 * Returns the component added.
581 *
582 * @param c the <code>Component</code> to add
583 * @return the <code>Component</code> added
584 */
585 public Component add(Component c) {
586 ensurePopupMenuCreated();
587 popupMenu.add(c);
588 return c;
589 }
590
591 /**
592 * Adds the specified component to this container at the given
593 * position. If <code>index</code> equals -1, the component will
594 * be appended to the end.
595 * @param c the <code>Component</code> to add
596 * @param index the position at which to insert the component
597 * @return the <code>Component</code> added
598 * @see #remove
599 * @see java.awt.Container#add(Component, int)
600 */
601 public Component add(Component c, int index) {
602 ensurePopupMenuCreated();
603 popupMenu.add(c, index);
604 return c;
605 }
606
607 /**
608 * Creates a new menu item with the specified text and appends
609 * it to the end of this menu.
610 *
611 * @param s the string for the menu item to be added
612 * @return the new {@code JMenuItem}
613 */
614 public JMenuItem add(String s) {
615 return add(new JMenuItem(s));
616 }
617
618 /**
619 * Creates a new menu item attached to the specified {@code Action} object
620 * and appends it to the end of this menu.
621 *
622 * @param a the {@code Action} for the menu item to be added
623 * @return the new {@code JMenuItem}
624 * @see Action
625 */
626 public JMenuItem add(Action a) {
627 JMenuItem mi = createActionComponent(a);
628 mi.setAction(a);
629 add(mi);
630 return mi;
631 }
632
633 /**
634 * Factory method which creates the <code>JMenuItem</code> for
635 * <code>Action</code>s added to the <code>JMenu</code>.
636 *
637 * @param a the <code>Action</code> for the menu item to be added
638 * @return the new menu item
639 * @see Action
640 *
641 * @since 1.3
642 */
643 protected JMenuItem createActionComponent(Action a) {
644 JMenuItem mi = new JMenuItem() {
645 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
646 PropertyChangeListener pcl = createActionChangeListener(this);
647 if (pcl == null) {
648 pcl = super.createActionPropertyChangeListener(a);
649 }
650 return pcl;
651 }
652 };
653 mi.setHorizontalTextPosition(JButton.TRAILING);
654 mi.setVerticalTextPosition(JButton.CENTER);
655 return mi;
656 }
657
666 return b.createActionPropertyChangeListener0(b.getAction());
667 }
668
669 /**
670 * Appends a new separator to the end of the menu.
671 */
672 public void addSeparator()
673 {
674 ensurePopupMenuCreated();
675 popupMenu.addSeparator();
676 }
677
678 /**
679 * Inserts a new menu item with the specified text at a
680 * given position.
681 *
682 * @param s the text for the menu item to add
683 * @param pos an integer specifying the position at which to add the
684 * new menu item
685 * @exception IllegalArgumentException when the value of
686 * <code>pos</code> < 0
687 */
688 public void insert(String s, int pos) {
689 if (pos < 0) {
690 throw new IllegalArgumentException("index less than zero.");
691 }
692
693 ensurePopupMenuCreated();
694 popupMenu.insert(new JMenuItem(s), pos);
695 }
696
697 /**
698 * Inserts the specified <code>JMenuitem</code> at a given position.
699 *
700 * @param mi the <code>JMenuitem</code> to add
701 * @param pos an integer specifying the position at which to add the
702 * new <code>JMenuitem</code>
703 * @return the new menu item
704 * @exception IllegalArgumentException if the value of
705 * <code>pos</code> < 0
706 */
707 public JMenuItem insert(JMenuItem mi, int pos) {
708 if (pos < 0) {
709 throw new IllegalArgumentException("index less than zero.");
710 }
711 ensurePopupMenuCreated();
712 popupMenu.insert(mi, pos);
713 return mi;
714 }
715
716 /**
717 * Inserts a new menu item attached to the specified <code>Action</code>
718 * object at a given position.
719 *
720 * @param a the <code>Action</code> object for the menu item to add
721 * @param pos an integer specifying the position at which to add the
722 * new menu item
723 * @return the new menu item
724 * @exception IllegalArgumentException if the value of
725 * <code>pos</code> < 0
726 */
727 public JMenuItem insert(Action a, int pos) {
728 if (pos < 0) {
729 throw new IllegalArgumentException("index less than zero.");
730 }
731
732 ensurePopupMenuCreated();
733 JMenuItem mi = new JMenuItem(a);
734 mi.setHorizontalTextPosition(JButton.TRAILING);
735 mi.setVerticalTextPosition(JButton.CENTER);
736 popupMenu.insert(mi, pos);
737 return mi;
738 }
739
740 /**
741 * Inserts a separator at the specified position.
742 *
743 * @param index an integer specifying the position at which to
744 * insert the menu separator
745 * @exception IllegalArgumentException if the value of
746 * <code>index</code> < 0
747 */
748 public void insertSeparator(int index) {
749 if (index < 0) {
750 throw new IllegalArgumentException("index less than zero.");
751 }
752
753 ensurePopupMenuCreated();
754 popupMenu.insert( new JPopupMenu.Separator(), index );
755 }
756
757 /**
758 * Returns the {@code JMenuItem} at the specified position.
759 * If the component at {@code pos} is not a menu item,
760 * {@code null} is returned.
761 * This method is included for AWT compatibility.
762 *
763 * @param pos an integer specifying the position
764 * @return the menu item at the specified position; or <code>null</code>
765 * if the item as the specified position is not a menu item
766 * @exception IllegalArgumentException if the value of
767 * {@code pos} < 0
768 */
769 public JMenuItem getItem(int pos) {
770 if (pos < 0) {
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 *
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
822 * <code>pos</code> < 0, or if <code>pos</code>
823 * is greater than the number of menu items
824 */
825 public void remove(int pos) {
826 if (pos < 0) {
827 throw new IllegalArgumentException("index less than zero.");
828 }
829 if (pos > getItemCount()) {
830 throw new IllegalArgumentException("index greater than the number of items.");
831 }
832 if (popupMenu != null)
833 popupMenu.remove(pos);
834 }
835
836 /**
837 * Removes the component <code>c</code> from this menu.
838 *
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())
924 return true;
925 }
926 // Are we in a Component on the PopupMenu
927 int ncomponents = this.getMenuComponentCount();
928 Component[] component = this.getMenuComponents();
929 for (int i = 0 ; i < ncomponents ; i++) {
930 Component comp = component[i];
931 // Are we in the current component?
932 if (comp == c)
933 return true;
934 // Hmmm, what about Non-menu containers?
935
936 // Recursive call for the Menu case
937 if (comp instanceof JMenu) {
938 JMenu subMenu = (JMenu) comp;
939 if (subMenu.isMenuComponent(c))
940 return true;
941 }
942 }
943 return false;
944 }
945
946
947 /*
948 * Returns a point in the coordinate space of this menu's popupmenu
949 * which corresponds to the point <code>p</code> in the menu's
950 * coordinate space.
951 *
952 * @param p the point to be translated
953 * @return the point in the coordinate space of this menu's popupmenu
954 */
955 private Point translateToPopupMenu(Point p) {
956 return translateToPopupMenu(p.x, p.y);
957 }
958
959 /*
960 * Returns a point in the coordinate space of this menu's popupmenu
961 * which corresponds to the point (x,y) in the menu's coordinate space.
962 *
963 * @param x the x coordinate of the point to be translated
964 * @param y the y coordinate of the point to be translated
965 * @return the point in the coordinate space of this menu's popupmenu
966 */
967 private Point translateToPopupMenu(int x, int y) {
968 int newX;
969 int newY;
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
1039 for (int i = listeners.length-2; i>=0; i-=2) {
1040 if (listeners[i]==MenuListener.class) {
1041 if (listeners[i+1]== null) {
1042 throw new Error(getText() +" has a NULL Listener!! " + i);
1043 } else {
1044 // Lazily create the event:
1045 if (menuEvent == null)
1046 menuEvent = new MenuEvent(this);
1047 ((MenuListener)listeners[i+1]).menuSelected(menuEvent);
1048 }
1049 }
1050 }
1051 }
1052
1053 /**
1054 * Notifies all listeners that have registered interest for
1055 * notification on this event type. The event instance
1056 * is created lazily.
1057 *
1058 * @exception Error if there is a <code>null</code> listener
1059 * @see EventListenerList
1060 */
1061 protected void fireMenuDeselected() {
1062 if (DEBUG) {
1063 System.out.println("In JMenu.fireMenuDeselected");
1064 }
1065 // Guaranteed to return a non-null array
1066 Object[] listeners = listenerList.getListenerList();
1067 // Process the listeners last to first, notifying
1068 // those that are interested in this event
1069 for (int i = listeners.length-2; i>=0; i-=2) {
1070 if (listeners[i]==MenuListener.class) {
1071 if (listeners[i+1]== null) {
1072 throw new Error(getText() +" has a NULL Listener!! " + i);
1073 } else {
1074 // Lazily create the event:
1075 if (menuEvent == null)
1076 menuEvent = new MenuEvent(this);
1077 ((MenuListener)listeners[i+1]).menuDeselected(menuEvent);
1078 }
1079 }
1080 }
1081 }
1082
1083 /**
1084 * Notifies all listeners that have registered interest for
1085 * notification on this event type. The event instance
1086 * is created lazily.
1087 *
1088 * @exception Error if there is a <code>null</code> listener
1089 * @see EventListenerList
1090 */
1091 protected void fireMenuCanceled() {
1092 if (DEBUG) {
1093 System.out.println("In JMenu.fireMenuCanceled");
1094 }
1095 // Guaranteed to return a non-null array
1096 Object[] listeners = listenerList.getListenerList();
1097 // Process the listeners last to first, notifying
1098 // those that are interested in this event
1099 for (int i = listeners.length-2; i>=0; i-=2) {
1100 if (listeners[i]==MenuListener.class) {
1101 if (listeners[i+1]== null) {
1102 throw new Error(getText() +" has a NULL Listener!! "
1103 + i);
1104 } else {
1105 // Lazily create the event:
1106 if (menuEvent == null)
1107 menuEvent = new MenuEvent(this);
1108 ((MenuListener)listeners[i+1]).menuCanceled(menuEvent);
1124
1125 if (modelSelected != isSelected) {
1126 if (modelSelected == true) {
1127 fireMenuSelected();
1128 } else {
1129 fireMenuDeselected();
1130 }
1131 isSelected = modelSelected;
1132 }
1133 }
1134 }
1135
1136 private ChangeListener createMenuChangeListener() {
1137 return new MenuChangeListener();
1138 }
1139
1140
1141 /**
1142 * Creates a window-closing listener for the popup.
1143 *
1144 * @param p the <code>JPopupMenu</code>
1145 * @return the new window-closing listener
1146 *
1147 * @see WinListener
1148 */
1149 protected WinListener createWinListener(JPopupMenu p) {
1150 return new WinListener(p);
1151 }
1152
1153 /**
1154 * A listener class that watches for a popup window closing.
1155 * When the popup is closing, the listener deselects the menu.
1156 * <p>
1157 * <strong>Warning:</strong>
1158 * Serialized objects of this class will not be compatible with
1159 * future Swing releases. The current serialization support is
1160 * appropriate for short term storage or RMI between applications running
1161 * the same version of Swing. As of 1.4, support for long term storage
1162 * of all JavaBeans™
1163 * has been added to the <code>java.beans</code> package.
1164 * Please see {@link java.beans.XMLEncoder}.
1165 */
1166 @SuppressWarnings("serial")
1167 protected class WinListener extends WindowAdapter implements Serializable {
1168 JPopupMenu popupMenu;
1169 /**
1170 * Create the window listener for the specified popup.
1171 *
1172 * @param p the popup menu for which to create a listener
1173 * @since 1.4
1174 */
1175 public WinListener(JPopupMenu p) {
1176 this.popupMenu = p;
1177 }
1178 /**
1179 * Deselect the menu when the popup is closed from outside.
1180 */
1181 public void windowClosing(WindowEvent e) {
1182 setSelected(false);
1183 }
1184 }
1185
1186 /**
1187 * Messaged when the menubar selection changes to activate or
1188 * deactivate this menu.
1189 * Overrides <code>JMenuItem.menuSelectionChanged</code>.
1190 *
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;
1231 }
1232
1233
1234 /**
1235 * Sets the <code>ComponentOrientation</code> property of this menu
1236 * and all components contained within it. This includes all
1237 * components returned by {@link #getMenuComponents getMenuComponents}.
1238 *
1239 * @param o the new component orientation of this menu and
1240 * the components contained within it.
1241 * @exception NullPointerException if <code>orientation</code> is null.
1242 * @see java.awt.Component#setComponentOrientation
1243 * @see java.awt.Component#getComponentOrientation
1244 * @since 1.4
1245 */
1246 public void applyComponentOrientation(ComponentOrientation o) {
1247 super.applyComponentOrientation(o);
1248
1249 if ( popupMenu != null ) {
1250 int ncomponents = getMenuComponentCount();
1251 for (int i = 0 ; i < ncomponents ; ++i) {
1252 getMenuComponent(i).applyComponentOrientation(o);
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
1298 * <code>AbstractButton.doClick</code> in order to make the menu pop up.
1299 * @param pressTime indicates the number of milliseconds the
1300 * button was pressed for
1301 */
1302 public void doClick(int pressTime) {
1303 MenuElement me[] = buildMenuElementArray(this);
1304 MenuSelectionManager.defaultManager().setSelectedPath(me);
1305 }
1306
1307 /*
1308 * Build an array of menu elements - from <code>PopupMenu</code> to
1309 * the root <code>JMenuBar</code>.
1310 * @param leaf the leaf node from which to start building up the array
1311 * @return the array of menu items
1312 */
1313 private MenuElement[] buildMenuElementArray(JMenu leaf) {
1314 Vector<MenuElement> elements = new Vector<MenuElement>();
1315 Component current = leaf.getPopupMenu();
1316 JPopupMenu pop;
1317 JMenu menu;
1318 JMenuBar bar;
1319
1320 while (true) {
1321 if (current instanceof JPopupMenu) {
1322 pop = (JPopupMenu) current;
1323 elements.insertElementAt(pop, 0);
1324 current = pop.getInvoker();
1325 } else if (current instanceof JMenu) {
1326 menu = (JMenu) current;
1327 elements.insertElementAt(menu, 0);
1328 current = menu.getParent();
1329 } else if (current instanceof JMenuBar) {
1330 bar = (JMenuBar) current;
1331 elements.insertElementAt(bar, 0);
1332 MenuElement me[] = new MenuElement[elements.size()];
1333 elements.copyInto(me);
1334 return me;
1335 }
1336 }
1337 }
1338
1339
1340 /**
1341 * See <code>readObject</code> and <code>writeObject</code> in
1342 * <code>JComponent</code> for more
1343 * information about serialization in Swing.
1344 */
1345 private void writeObject(ObjectOutputStream s) throws IOException {
1346 s.defaultWriteObject();
1347 if (getUIClassID().equals(uiClassID)) {
1348 byte count = JComponent.getWriteObjCounter(this);
1349 JComponent.setWriteObjCounter(this, --count);
1350 if (count == 0 && ui != null) {
1351 ui.installUI(this);
1352 }
1353 }
1354 }
1355
1356
1357 /**
1358 * Returns a string representation of this <code>JMenu</code>. This
1359 * method is intended to be used only for debugging purposes, and the
1360 * content and format of the returned string may vary between
1361 * implementations. The returned string may be empty but may not
1362 * be <code>null</code>.
1363 *
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}.
1404 */
1405 @SuppressWarnings("serial")
1406 protected class AccessibleJMenu extends AccessibleJMenuItem
1407 implements AccessibleSelection {
1408
1409 /**
1410 * Returns the number of accessible children in the object. If all
1411 * of the children of this object implement Accessible, than this
1412 * method should return the number of children of this object.
1413 *
1414 * @return the number of accessible children in the object.
1415 */
1416 public int getAccessibleChildrenCount() {
1417 Component[] children = getMenuComponents();
1418 int count = 0;
1419 for (Component child : children) {
1420 if (child instanceof Accessible) {
1421 count++;
1422 }
1533 * object.
1534 * @see AccessibleContext#getAccessibleChild
1535 */
1536 public boolean isAccessibleChildSelected(int i) {
1537 // if i is a sub-menu and is pop-ed up, return true, else false
1538 MenuElement me[] =
1539 MenuSelectionManager.defaultManager().getSelectedPath();
1540 if (me != null) {
1541 JMenuItem mi = JMenu.this.getItem(i);
1542 for (int j = 0; j < me.length; j++) {
1543 if (me[j] == mi) {
1544 return true;
1545 }
1546 }
1547 }
1548 return false;
1549 }
1550
1551
1552 /**
1553 * Selects the <code>i</code>th menu in the menu.
1554 * If that item is a submenu,
1555 * it will pop up in response. If a different item is already
1556 * popped up, this will force it to close. If this is a sub-menu
1557 * that is already popped up (selected), this method has no
1558 * effect.
1559 *
1560 * @param i the index of the item to be selected
1561 * @see #getAccessibleStateSet
1562 */
1563 public void addAccessibleSelection(int i) {
1564 if (i < 0 || i >= getItemCount()) {
1565 return;
1566 }
1567 JMenuItem mi = getItem(i);
1568 if (mi != null) {
1569 if (mi instanceof JMenu) {
1570 MenuElement me[] = buildMenuElementArray((JMenu) mi);
1571 MenuSelectionManager.defaultManager().setSelectedPath(me);
1572 } else {
1573 MenuSelectionManager.defaultManager().setSelectedPath(null);
|
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}s that
63 * is displayed when the user selects an item on the {@code JMenuBar}.
64 * In addition to {@code JMenuItem}s, a {@code JMenu} can
65 * also contain {@code JSeparator}s.
66 * <p>
67 * In essence, a menu is a button with an associated {@code JPopupMenu}.
68 * When the "button" is pressed, the {@code JPopupMenu} appears. If the
69 * "button" is on the {@code JMenuBar}, the menu is a top-level window.
70 * If the "button" is another menu item, then the {@code JPopupMenu} 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} 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}</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} 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}.
129 */
130 private ChangeListener menuChangeListener = null;
131
132 /*
133 * Only one {@code MenuEvent} is needed for each menu since the
134 * event's only state is the source property. The source of events
135 * generated is always "this". Default is {@code null}.
136 */
137 private MenuEvent menuEvent = null;
138
139 /*
140 * Used by the look and feel (L&F) code to handle
141 * implementation specific menu behaviors.
142 */
143 private int delay;
144
145 /*
146 * Location of the popup component. Location is {@code null}
147 * if it was not customized by {@code setMenuLocation}
148 */
149 private Point customMenuLocation = null;
150
151 /* Diagnostic aids -- should be false for production builds. */
152 private static final boolean TRACE = false; // trace creates and disposes
153 private static final boolean VERBOSE = false; // show reuse hits/misses
154 private static final boolean DEBUG = false; // show bad params, misc.
155
156 /**
157 * Constructs a new {@code JMenu} with no text.
158 */
159 public JMenu() {
160 this("");
161 }
162
163 /**
164 * Constructs a new {@code JMenu} with the supplied string
165 * as its text.
166 *
167 * @param s the text for the menu label
168 */
169 public JMenu(String s) {
170 super(s);
171 }
172
173 /**
174 * Constructs a menu whose properties are taken from the
175 * {@code Action} supplied.
176 * @param a an {@code Action}
177 *
178 * @since 1.3
179 */
180 public JMenu(Action a) {
181 this();
182 setAction(a);
183 }
184
185 /**
186 * Constructs a new {@code JMenu} with the supplied string as
187 * its text and specified as a tear-off menu or not.
188 *
189 * @param s the text for the menu label
190 * @param b can the menu be torn off (not yet implemented)
191 */
192 public JMenu(String s, boolean b) {
193 this(s);
194 }
195
196
197 /**
198 * Overriden to do nothing. We want JMenu to be focusable, but
199 * {@code JMenuItem} doesn't want to be, thus we override this
200 * do nothing. We don't invoke {@code setFocusable(true)} after
201 * super's constructor has completed as this has the side effect that
202 * {@code JMenu} will be considered traversable via the
203 * keyboard, which we don't want. Making a Component traversable by
204 * the keyboard after invoking {@code setFocusable(true)} is OK,
205 * as {@code setFocusable} is new API
206 * and is speced as such, but internally we don't want to use it like
207 * this else we change the keyboard traversability.
208 */
209 void initFocusability() {
210 }
211
212 /**
213 * Resets the UI property with a value from the current look and feel.
214 *
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 }
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}
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) {
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 }
351 }
352
353 /**
354 * Computes the origin for the {@code JMenu}'s popup menu.
355 * This method uses Look and Feel properties named
356 * {@code Menu.menuPopupOffsetX},
357 * {@code Menu.menuPopupOffsetY},
358 * {@code Menu.submenuPopupOffsetX}, and
359 * {@code Menu.submenuPopupOffsetY}
360 * to adjust the exact location of popup.
361 *
362 * @return a {@code Point} in the coordinate space of the
363 * menu which should be used as the origin
364 * of the {@code JMenu}'s popup menu
365 *
366 * @since 1.3
367 */
368 protected Point getPopupMenuOrigin() {
369 int x;
370 int y;
371 JPopupMenu pm = getPopupMenu();
372 // Figure out the sizes needed to caclulate the menu position
373 Dimension s = getSize();
374 Dimension pmSize = pm.getSize();
375 // For the first time the menu is popped up,
376 // the size has not yet been initiated
377 if (pmSize.width==0) {
378 pmSize = pm.getPreferredSize();
379 }
380 Point position = getLocationOnScreen();
381 Toolkit toolkit = Toolkit.getDefaultToolkit();
382 GraphicsConfiguration gc = getGraphicsConfiguration();
383 Rectangle screenBounds = new Rectangle(toolkit.getScreenSize());
384 GraphicsEnvironment ge =
476 }
477 // Then the y:
478 y = s.height + yOffset; // Prefer dropping down
479 if (position.y + y + pmSize.height >= screenBounds.height
480 + screenBounds.y &&
481 // popup doesn't fit - place it wherever there's more room
482 screenBounds.height - s.height < 2*(position.y
483 - screenBounds.y)) {
484
485 y = 0 - yOffset - pmSize.height; // Otherwise drop 'up'
486 }
487 }
488 return new Point(x,y);
489 }
490
491
492 /**
493 * Returns the suggested delay, in milliseconds, before submenus
494 * are popped up or down.
495 * Each look and feel (L&F) may determine its own policy for
496 * observing the {@code delay} property.
497 * In most cases, the delay is not observed for top level menus
498 * or while dragging. The default for {@code delay} is 0.
499 * This method is a property of the look and feel code and is used
500 * to manage the idiosyncrasies of the various UI implementations.
501 *
502 *
503 * @return the {@code delay} property
504 */
505 public int getDelay() {
506 return delay;
507 }
508
509 /**
510 * Sets the suggested delay before the menu's {@code PopupMenu}
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}
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() {
550 private Point getCustomMenuLocation() {
551 return customMenuLocation;
552 }
553
554 /**
555 * Sets the location of the popup component.
556 *
557 * @param x the x coordinate of the popup's new position
558 * @param y the y coordinate of the popup's new position
559 */
560 public void setMenuLocation(int x, int y) {
561 customMenuLocation = new Point(x, y);
562 if (popupMenu != null)
563 popupMenu.setLocation(x, y);
564 }
565
566 /**
567 * Appends a menu item to the end of this menu.
568 * Returns the menu item added.
569 *
570 * @param menuItem the {@code JMenuitem} to be added
571 * @return the {@code JMenuItem} added
572 */
573 public JMenuItem add(JMenuItem menuItem) {
574 ensurePopupMenuCreated();
575 return popupMenu.add(menuItem);
576 }
577
578 /**
579 * Appends a component to the end of this menu.
580 * Returns the component added.
581 *
582 * @param c the {@code Component} to add
583 * @return the {@code Component} added
584 */
585 public Component add(Component c) {
586 ensurePopupMenuCreated();
587 popupMenu.add(c);
588 return c;
589 }
590
591 /**
592 * Adds the specified component to this container at the given
593 * position. If {@code index} equals -1, the component will
594 * be appended to the end.
595 * @param c the {@code Component} to add
596 * @param index the position at which to insert the component
597 * @return the {@code Component} added
598 * @see #remove
599 * @see java.awt.Container#add(Component, int)
600 */
601 public Component add(Component c, int index) {
602 ensurePopupMenuCreated();
603 popupMenu.add(c, index);
604 return c;
605 }
606
607 /**
608 * Creates a new menu item with the specified text and appends
609 * it to the end of this menu.
610 *
611 * @param s the string for the menu item to be added
612 * @return the new {@code JMenuItem}
613 */
614 public JMenuItem add(String s) {
615 return add(new JMenuItem(s));
616 }
617
618 /**
619 * Creates a new menu item attached to the specified {@code Action} object
620 * and appends it to the end of this menu.
621 *
622 * @param a the {@code Action} for the menu item to be added
623 * @return the new {@code JMenuItem}
624 * @see Action
625 */
626 public JMenuItem add(Action a) {
627 JMenuItem mi = createActionComponent(a);
628 mi.setAction(a);
629 add(mi);
630 return mi;
631 }
632
633 /**
634 * Factory method which creates the {@code JMenuItem} for
635 * {@code Action}s added to the {@code JMenu}.
636 *
637 * @param a the {@code Action} for the menu item to be added
638 * @return the new menu item
639 * @see Action
640 *
641 * @since 1.3
642 */
643 protected JMenuItem createActionComponent(Action a) {
644 JMenuItem mi = new JMenuItem() {
645 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
646 PropertyChangeListener pcl = createActionChangeListener(this);
647 if (pcl == null) {
648 pcl = super.createActionPropertyChangeListener(a);
649 }
650 return pcl;
651 }
652 };
653 mi.setHorizontalTextPosition(JButton.TRAILING);
654 mi.setVerticalTextPosition(JButton.CENTER);
655 return mi;
656 }
657
666 return b.createActionPropertyChangeListener0(b.getAction());
667 }
668
669 /**
670 * Appends a new separator to the end of the menu.
671 */
672 public void addSeparator()
673 {
674 ensurePopupMenuCreated();
675 popupMenu.addSeparator();
676 }
677
678 /**
679 * Inserts a new menu item with the specified text at a
680 * given position.
681 *
682 * @param s the text for the menu item to add
683 * @param pos an integer specifying the position at which to add the
684 * new menu item
685 * @exception IllegalArgumentException when the value of
686 * {@code pos < 0}
687 */
688 public void insert(String s, int pos) {
689 if (pos < 0) {
690 throw new IllegalArgumentException("index less than zero.");
691 }
692
693 ensurePopupMenuCreated();
694 popupMenu.insert(new JMenuItem(s), pos);
695 }
696
697 /**
698 * Inserts the specified {@code JMenuitem} at a given position.
699 *
700 * @param mi the {@code JMenuitem} to add
701 * @param pos an integer specifying the position at which to add the
702 * new {@code JMenuitem}
703 * @return the new menu item
704 * @exception IllegalArgumentException if the value of
705 * {@code pos < 0}
706 */
707 public JMenuItem insert(JMenuItem mi, int pos) {
708 if (pos < 0) {
709 throw new IllegalArgumentException("index less than zero.");
710 }
711 ensurePopupMenuCreated();
712 popupMenu.insert(mi, pos);
713 return mi;
714 }
715
716 /**
717 * Inserts a new menu item attached to the specified {@code Action}
718 * object at a given position.
719 *
720 * @param a the {@code Action} object for the menu item to add
721 * @param pos an integer specifying the position at which to add the
722 * new menu item
723 * @return the new menu item
724 * @exception IllegalArgumentException if the value of
725 * {@code pos < 0}
726 */
727 public JMenuItem insert(Action a, int pos) {
728 if (pos < 0) {
729 throw new IllegalArgumentException("index less than zero.");
730 }
731
732 ensurePopupMenuCreated();
733 JMenuItem mi = new JMenuItem(a);
734 mi.setHorizontalTextPosition(JButton.TRAILING);
735 mi.setVerticalTextPosition(JButton.CENTER);
736 popupMenu.insert(mi, pos);
737 return mi;
738 }
739
740 /**
741 * Inserts a separator at the specified position.
742 *
743 * @param index an integer specifying the position at which to
744 * insert the menu separator
745 * @exception IllegalArgumentException if the value of
746 * {@code index < 0}
747 */
748 public void insertSeparator(int index) {
749 if (index < 0) {
750 throw new IllegalArgumentException("index less than zero.");
751 }
752
753 ensurePopupMenuCreated();
754 popupMenu.insert( new JPopupMenu.Separator(), index );
755 }
756
757 /**
758 * Returns the {@code JMenuItem} at the specified position.
759 * If the component at {@code pos} is not a menu item,
760 * {@code null} is returned.
761 * This method is included for AWT compatibility.
762 *
763 * @param pos an integer specifying the position
764 * @return the menu item at the specified position; or {@code null}
765 * if the item as the specified position is not a menu item
766 * @exception IllegalArgumentException if the value of
767 * {@code pos < 0}
768 */
769 public JMenuItem getItem(int pos) {
770 if (pos < 0) {
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 *
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} 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
822 * {@code pos < 0}, or if {@code pos}
823 * is greater than the number of menu items
824 */
825 public void remove(int pos) {
826 if (pos < 0) {
827 throw new IllegalArgumentException("index less than zero.");
828 }
829 if (pos > getItemCount()) {
830 throw new IllegalArgumentException("index greater than the number of items.");
831 }
832 if (popupMenu != null)
833 popupMenu.remove(pos);
834 }
835
836 /**
837 * Removes the component {@code c} from this menu.
838 *
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}.
868 *
869 * @param n the position of the component to be returned
870 * @return the component requested, or {@code null}
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}s of the menu's
883 * subcomponents. Note that this returns all {@code Component}s
884 * in the popup menu, including separators.
885 *
886 * @return an array of {@code Component}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} to be tested
914 * @return true if the {@code Component} 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())
924 return true;
925 }
926 // Are we in a Component on the PopupMenu
927 int ncomponents = this.getMenuComponentCount();
928 Component[] component = this.getMenuComponents();
929 for (int i = 0 ; i < ncomponents ; i++) {
930 Component comp = component[i];
931 // Are we in the current component?
932 if (comp == c)
933 return true;
934 // Hmmm, what about Non-menu containers?
935
936 // Recursive call for the Menu case
937 if (comp instanceof JMenu) {
938 JMenu subMenu = (JMenu) comp;
939 if (subMenu.isMenuComponent(c))
940 return true;
941 }
942 }
943 return false;
944 }
945
946
947 /*
948 * Returns a point in the coordinate space of this menu's popupmenu
949 * which corresponds to the point {@code p} in the menu's
950 * coordinate space.
951 *
952 * @param p the point to be translated
953 * @return the point in the coordinate space of this menu's popupmenu
954 */
955 private Point translateToPopupMenu(Point p) {
956 return translateToPopupMenu(p.x, p.y);
957 }
958
959 /*
960 * Returns a point in the coordinate space of this menu's popupmenu
961 * which corresponds to the point (x,y) in the menu's coordinate space.
962 *
963 * @param x the x coordinate of the point to be translated
964 * @param y the y coordinate of the point to be translated
965 * @return the point in the coordinate space of this menu's popupmenu
966 */
967 private Point translateToPopupMenu(int x, int y) {
968 int newX;
969 int newY;
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}s added
1013 * to this JMenu with addMenuListener().
1014 *
1015 * @return all of the {@code MenuListener}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} 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
1039 for (int i = listeners.length-2; i>=0; i-=2) {
1040 if (listeners[i]==MenuListener.class) {
1041 if (listeners[i+1]== null) {
1042 throw new Error(getText() +" has a NULL Listener!! " + i);
1043 } else {
1044 // Lazily create the event:
1045 if (menuEvent == null)
1046 menuEvent = new MenuEvent(this);
1047 ((MenuListener)listeners[i+1]).menuSelected(menuEvent);
1048 }
1049 }
1050 }
1051 }
1052
1053 /**
1054 * Notifies all listeners that have registered interest for
1055 * notification on this event type. The event instance
1056 * is created lazily.
1057 *
1058 * @exception Error if there is a {@code null} listener
1059 * @see EventListenerList
1060 */
1061 protected void fireMenuDeselected() {
1062 if (DEBUG) {
1063 System.out.println("In JMenu.fireMenuDeselected");
1064 }
1065 // Guaranteed to return a non-null array
1066 Object[] listeners = listenerList.getListenerList();
1067 // Process the listeners last to first, notifying
1068 // those that are interested in this event
1069 for (int i = listeners.length-2; i>=0; i-=2) {
1070 if (listeners[i]==MenuListener.class) {
1071 if (listeners[i+1]== null) {
1072 throw new Error(getText() +" has a NULL Listener!! " + i);
1073 } else {
1074 // Lazily create the event:
1075 if (menuEvent == null)
1076 menuEvent = new MenuEvent(this);
1077 ((MenuListener)listeners[i+1]).menuDeselected(menuEvent);
1078 }
1079 }
1080 }
1081 }
1082
1083 /**
1084 * Notifies all listeners that have registered interest for
1085 * notification on this event type. The event instance
1086 * is created lazily.
1087 *
1088 * @exception Error if there is a {@code null} listener
1089 * @see EventListenerList
1090 */
1091 protected void fireMenuCanceled() {
1092 if (DEBUG) {
1093 System.out.println("In JMenu.fireMenuCanceled");
1094 }
1095 // Guaranteed to return a non-null array
1096 Object[] listeners = listenerList.getListenerList();
1097 // Process the listeners last to first, notifying
1098 // those that are interested in this event
1099 for (int i = listeners.length-2; i>=0; i-=2) {
1100 if (listeners[i]==MenuListener.class) {
1101 if (listeners[i+1]== null) {
1102 throw new Error(getText() +" has a NULL Listener!! "
1103 + i);
1104 } else {
1105 // Lazily create the event:
1106 if (menuEvent == null)
1107 menuEvent = new MenuEvent(this);
1108 ((MenuListener)listeners[i+1]).menuCanceled(menuEvent);
1124
1125 if (modelSelected != isSelected) {
1126 if (modelSelected == true) {
1127 fireMenuSelected();
1128 } else {
1129 fireMenuDeselected();
1130 }
1131 isSelected = modelSelected;
1132 }
1133 }
1134 }
1135
1136 private ChangeListener createMenuChangeListener() {
1137 return new MenuChangeListener();
1138 }
1139
1140
1141 /**
1142 * Creates a window-closing listener for the popup.
1143 *
1144 * @param p the {@code JPopupMenu}
1145 * @return the new window-closing listener
1146 *
1147 * @see WinListener
1148 */
1149 protected WinListener createWinListener(JPopupMenu p) {
1150 return new WinListener(p);
1151 }
1152
1153 /**
1154 * A listener class that watches for a popup window closing.
1155 * When the popup is closing, the listener deselects the menu.
1156 * <p>
1157 * <strong>Warning:</strong>
1158 * Serialized objects of this class will not be compatible with
1159 * future Swing releases. The current serialization support is
1160 * appropriate for short term storage or RMI between applications running
1161 * the same version of Swing. As of 1.4, support for long term storage
1162 * of all JavaBeans™
1163 * has been added to the {@code java.beans} package.
1164 * Please see {@link java.beans.XMLEncoder}.
1165 */
1166 @SuppressWarnings("serial")
1167 protected class WinListener extends WindowAdapter implements Serializable {
1168 JPopupMenu popupMenu;
1169 /**
1170 * Create the window listener for the specified popup.
1171 *
1172 * @param p the popup menu for which to create a listener
1173 * @since 1.4
1174 */
1175 public WinListener(JPopupMenu p) {
1176 this.popupMenu = p;
1177 }
1178 /**
1179 * Deselect the menu when the popup is closed from outside.
1180 */
1181 public void windowClosing(WindowEvent e) {
1182 setSelected(false);
1183 }
1184 }
1185
1186 /**
1187 * Messaged when the menubar selection changes to activate or
1188 * deactivate this menu.
1189 * Overrides {@code JMenuItem.menuSelectionChanged}.
1190 *
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}s containing the submenu
1203 * for this menu component. If popup menu is {@code null} returns
1204 * an empty array. This method is required to conform to the
1205 * {@code MenuElement} interface. Note that since
1206 * {@code JSeparator}s do not conform to the {@code MenuElement}
1207 * interface, this array will only contain {@code JMenuItem}s.
1208 *
1209 * @return an array of {@code MenuElement} 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} used to
1225 * paint this {@code MenuElement}.
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;
1231 }
1232
1233
1234 /**
1235 * Sets the {@code ComponentOrientation} property of this menu
1236 * and all components contained within it. This includes all
1237 * components returned by {@link #getMenuComponents getMenuComponents}.
1238 *
1239 * @param o the new component orientation of this menu and
1240 * the components contained within it.
1241 * @exception NullPointerException if {@code orientation} is null.
1242 * @see java.awt.Component#setComponentOrientation
1243 * @see java.awt.Component#getComponentOrientation
1244 * @since 1.4
1245 */
1246 public void applyComponentOrientation(ComponentOrientation o) {
1247 super.applyComponentOrientation(o);
1248
1249 if ( popupMenu != null ) {
1250 int ncomponents = getMenuComponentCount();
1251 for (int i = 0 ; i < ncomponents ; ++i) {
1252 getMenuComponent(i).applyComponentOrientation(o);
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} is not defined for {@code JMenu}.
1267 * Use {@code setMnemonic} instead.
1268 * @param keyStroke the keystroke combination which will invoke
1269 * the {@code JMenuItem}'s actionlisteners
1270 * without navigating the menu hierarchy
1271 * @exception Error if invoked -- this method is not defined for JMenu.
1272 * Use {@code setMnemonic} 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
1298 * {@code AbstractButton.doClick} in order to make the menu pop up.
1299 * @param pressTime indicates the number of milliseconds the
1300 * button was pressed for
1301 */
1302 public void doClick(int pressTime) {
1303 MenuElement me[] = buildMenuElementArray(this);
1304 MenuSelectionManager.defaultManager().setSelectedPath(me);
1305 }
1306
1307 /*
1308 * Build an array of menu elements - from {@code PopupMenu} to
1309 * the root {@code JMenuBar}.
1310 * @param leaf the leaf node from which to start building up the array
1311 * @return the array of menu items
1312 */
1313 private MenuElement[] buildMenuElementArray(JMenu leaf) {
1314 Vector<MenuElement> elements = new Vector<MenuElement>();
1315 Component current = leaf.getPopupMenu();
1316 JPopupMenu pop;
1317 JMenu menu;
1318 JMenuBar bar;
1319
1320 while (true) {
1321 if (current instanceof JPopupMenu) {
1322 pop = (JPopupMenu) current;
1323 elements.insertElementAt(pop, 0);
1324 current = pop.getInvoker();
1325 } else if (current instanceof JMenu) {
1326 menu = (JMenu) current;
1327 elements.insertElementAt(menu, 0);
1328 current = menu.getParent();
1329 } else if (current instanceof JMenuBar) {
1330 bar = (JMenuBar) current;
1331 elements.insertElementAt(bar, 0);
1332 MenuElement me[] = new MenuElement[elements.size()];
1333 elements.copyInto(me);
1334 return me;
1335 }
1336 }
1337 }
1338
1339
1340 /**
1341 * See {@code readObject} and {@code writeObject} in
1342 * {@code JComponent} for more
1343 * information about serialization in Swing.
1344 */
1345 private void writeObject(ObjectOutputStream s) throws IOException {
1346 s.defaultWriteObject();
1347 if (getUIClassID().equals(uiClassID)) {
1348 byte count = JComponent.getWriteObjCounter(this);
1349 JComponent.setWriteObjCounter(this, --count);
1350 if (count == 0 && ui != null) {
1351 ui.installUI(this);
1352 }
1353 }
1354 }
1355
1356
1357 /**
1358 * Returns a string representation of this {@code JMenu}. This
1359 * method is intended to be used only for debugging purposes, and the
1360 * content and format of the returned string may vary between
1361 * implementations. The returned string may be empty but may not
1362 * be {@code null}.
1363 *
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} 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} package.
1403 * Please see {@link java.beans.XMLEncoder}.
1404 */
1405 @SuppressWarnings("serial")
1406 protected class AccessibleJMenu extends AccessibleJMenuItem
1407 implements AccessibleSelection {
1408
1409 /**
1410 * Returns the number of accessible children in the object. If all
1411 * of the children of this object implement Accessible, than this
1412 * method should return the number of children of this object.
1413 *
1414 * @return the number of accessible children in the object.
1415 */
1416 public int getAccessibleChildrenCount() {
1417 Component[] children = getMenuComponents();
1418 int count = 0;
1419 for (Component child : children) {
1420 if (child instanceof Accessible) {
1421 count++;
1422 }
1533 * object.
1534 * @see AccessibleContext#getAccessibleChild
1535 */
1536 public boolean isAccessibleChildSelected(int i) {
1537 // if i is a sub-menu and is pop-ed up, return true, else false
1538 MenuElement me[] =
1539 MenuSelectionManager.defaultManager().getSelectedPath();
1540 if (me != null) {
1541 JMenuItem mi = JMenu.this.getItem(i);
1542 for (int j = 0; j < me.length; j++) {
1543 if (me[j] == mi) {
1544 return true;
1545 }
1546 }
1547 }
1548 return false;
1549 }
1550
1551
1552 /**
1553 * Selects the {@code i}th menu in the menu.
1554 * If that item is a submenu,
1555 * it will pop up in response. If a different item is already
1556 * popped up, this will force it to close. If this is a sub-menu
1557 * that is already popped up (selected), this method has no
1558 * effect.
1559 *
1560 * @param i the index of the item to be selected
1561 * @see #getAccessibleStateSet
1562 */
1563 public void addAccessibleSelection(int i) {
1564 if (i < 0 || i >= getItemCount()) {
1565 return;
1566 }
1567 JMenuItem mi = getItem(i);
1568 if (mi != null) {
1569 if (mi instanceof JMenu) {
1570 MenuElement me[] = buildMenuElementArray((JMenu) mi);
1571 MenuSelectionManager.defaultManager().setSelectedPath(me);
1572 } else {
1573 MenuSelectionManager.defaultManager().setSelectedPath(null);
|