KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > JMenu


1 /*
2  * @(#)JMenu.java 1.172 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing;
9
10 import java.awt.AWTEvent JavaDoc;
11 import java.awt.Component JavaDoc;
12 import java.awt.ComponentOrientation JavaDoc;
13 import java.awt.Container JavaDoc;
14 import java.awt.Dimension JavaDoc;
15 import java.awt.Frame JavaDoc;
16 import java.awt.Graphics JavaDoc;
17 import java.awt.GraphicsConfiguration JavaDoc;
18 import java.awt.GraphicsDevice JavaDoc;
19 import java.awt.GraphicsEnvironment JavaDoc;
20 import java.awt.Insets JavaDoc;
21 import java.awt.Point JavaDoc;
22 import java.awt.Polygon JavaDoc;
23 import java.awt.Rectangle JavaDoc;
24 import java.awt.Toolkit JavaDoc;
25 import java.awt.event.*;
26 import java.beans.*;
27
28 import java.util.*;
29
30 import java.io.Serializable JavaDoc;
31 import java.io.ObjectOutputStream JavaDoc;
32 import java.io.ObjectInputStream JavaDoc;
33 import java.io.IOException JavaDoc;
34
35 import javax.swing.event.*;
36 import javax.swing.plaf.*;
37 import javax.swing.plaf.basic.*;
38 import javax.accessibility.*;
39
40 import java.lang.ref.WeakReference JavaDoc;
41
42 /**
43  * An implementation of a menu -- a popup window containing
44  * <code>JMenuItem</code>s that
45  * is displayed when the user selects an item on the <code>JMenuBar</code>.
46  * In addition to <code>JMenuItem</code>s, a <code>JMenu</code> can
47  * also contain <code>JSeparator</code>s.
48  * <p>
49  * In essence, a menu is a button with an associated <code>JPopupMenu</code>.
50  * When the "button" is pressed, the <code>JPopupMenu</code> appears. If the
51  * "button" is on the <code>JMenuBar</code>, the menu is a top-level window.
52  * If the "button" is another menu item, then the <code>JPopupMenu</code> is
53  * "pull-right" menu.
54  * <p>
55  * For information and examples of using menus see
56  * <a HREF="http://java.sun.com/doc/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
57  * a section in <em>The Java Tutorial.</em>
58  * <p>
59  * <strong>Warning:</strong>
60  * Serialized objects of this class will not be compatible with
61  * future Swing releases. The current serialization support is
62  * appropriate for short term storage or RMI between applications running
63  * the same version of Swing. As of 1.4, support for long term storage
64  * of all JavaBeans<sup><font size="-2">TM</font></sup>
65  * has been added to the <code>java.beans</code> package.
66  * Please see {@link java.beans.XMLEncoder}.
67  *
68  * @beaninfo
69  * attribute: isContainer true
70  * description: A popup window containing menu items displayed in a menu bar.
71  *
72  * @version 1.172 12/19/03
73  * @author Georges Saab
74  * @author David Karlton
75  * @author Arnaud Weber
76  * @see JMenuItem
77  * @see JSeparator
78  * @see JMenuBar
79  * @see JPopupMenu
80  */

81 public class JMenu extends JMenuItem JavaDoc implements Accessible,MenuElement JavaDoc
82 {
83     /**
84      * @see #getUIClassID
85      * @see #readObject
86      */

87     private static final String JavaDoc uiClassID = "MenuUI";
88
89     /*
90      * The popup menu portion of the menu.
91      */

92     private JPopupMenu JavaDoc popupMenu;
93
94     /*
95      * The button's model listeners. Default is <code>null</code>.
96      */

97     private ChangeListener menuChangeListener = null;
98
99     /*
100      * Only one <code>MenuEvent</code> is needed for each menu since the
101      * event's only state is the source property. The source of events
102      * generated is always "this". Default is <code>null</code>.
103      */

104     private MenuEvent menuEvent = null;
105
106     /* Registry of listeners created for <code>Action-JMenuItem</code>
107      * linkage. This is needed so that references can
108      * be cleaned up at remove time to allow garbage collection
109      * Default is <code>null</code>.
110      */

111     private static Hashtable listenerRegistry = null;
112
113     /*
114      * Used by the look and feel (L&F) code to handle
115      * implementation specific menu behaviors.
116      */

117     private int delay;
118
119      /*
120       * Location of the popup component. Location is <code>null</code>
121       * if it was not customized by <code>setMenuLocation</code>
122       */

123      private Point JavaDoc customMenuLocation = null;
124
125     /* Diagnostic aids -- should be false for production builds. */
126     private static final boolean TRACE = false; // trace creates and disposes
127
private static final boolean VERBOSE = false; // show reuse hits/misses
128
private static final boolean DEBUG = false; // show bad params, misc.
129

130     /**
131      * Constructs a new <code>JMenu</code> with no text.
132      */

133     public JMenu() {
134         this("");
135     }
136
137     /**
138      * Constructs a new <code>JMenu</code> with the supplied string
139      * as its text.
140      *
141      * @param s the text for the menu label
142      */

143     public JMenu(String JavaDoc s) {
144     super(s);
145     }
146
147     /**
148      * Constructs a menu whose properties are taken from the
149      * <code>Action</code> supplied.
150      * @param a an <code>Action</code>
151      *
152      * @since 1.3
153      */

154     public JMenu(Action JavaDoc a) {
155         this();
156     setAction(a);
157     }
158
159     /**
160      * Constructs a new <code>JMenu</code> with the supplied string as
161      * its text and specified as a tear-off menu or not.
162      *
163      * @param s the text for the menu label
164      * @param b can the menu be torn off (not yet implemented)
165      */

166     public JMenu(String JavaDoc s, boolean b) {
167         this(s);
168     }
169
170
171     /**
172      * Overriden to do nothing. We want JMenu to be focusable, but
173      * <code>JMenuItem</code> doesn't want to be, thus we override this
174      * do nothing. We don't invoke <code>setFocusable(true)</code> after
175      * super's constructor has completed as this has the side effect that
176      * <code>JMenu</code> will be considered traversable via the
177      * keyboard, which we don't want. Making a Component traversable by
178      * the keyboard after invoking <code>setFocusable(true)</code> is OK,
179      * as <code>setFocusable</code> is new API
180      * and is speced as such, but internally we don't want to use it like
181      * this else we change the keyboard traversability.
182      */

183     void initFocusability() {
184     }
185
186     /**
187      * Resets the UI property with a value from the current look and feel.
188      *
189      * @see JComponent#updateUI
190      */

191     public void updateUI() {
192         setUI((MenuItemUI)UIManager.getUI(this));
193
194         if ( popupMenu != null )
195           {
196             popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu));
197           }
198
199     }
200
201
202     /**
203      * Returns the name of the L&F class that renders this component.
204      *
205      * @return the string "MenuUI"
206      * @see JComponent#getUIClassID
207      * @see UIDefaults#getUI
208      */

209     public String JavaDoc getUIClassID() {
210         return uiClassID;
211     }
212
213     // public void repaint(long tm, int x, int y, int width, int height) {
214
// Thread.currentThread().dumpStack();
215
// super.repaint(tm,x,y,width,height);
216
// }
217

218     /**
219      * Sets the data model for the "menu button" -- the label
220      * that the user clicks to open or close the menu.
221      *
222      * @param newModel the <code>ButtonModel</code>
223      * @see #getModel
224      * @beaninfo
225      * description: The menu's model
226      * bound: true
227      * expert: true
228      * hidden: true
229      */

230     public void setModel(ButtonModel JavaDoc newModel) {
231         ButtonModel JavaDoc oldModel = getModel();
232
233         super.setModel(newModel);
234
235         if (oldModel != null && menuChangeListener != null) {
236             oldModel.removeChangeListener(menuChangeListener);
237             menuChangeListener = null;
238         }
239         
240         model = newModel;
241         
242         if (newModel != null) {
243             menuChangeListener = createMenuChangeListener();
244             newModel.addChangeListener(menuChangeListener);
245         }
246     }
247
248     /**
249      * Returns true if the menu is currently selected (highlighted).
250      *
251      * @return true if the menu is selected, else false
252      */

253     public boolean isSelected() {
254         return getModel().isSelected();
255     }
256
257     /**
258      * Sets the selection status of the menu.
259      *
260      * @param b true to select (highlight) the menu; false to de-select
261      * the menu
262      * @beaninfo
263      * description: When the menu is selected, its popup child is shown.
264      * expert: true
265      * hidden: true
266      */

267     public void setSelected(boolean b) {
268         ButtonModel JavaDoc model = getModel();
269         boolean oldValue = model.isSelected();
270
271         // TIGER - 4840653
272
// Removed code which fired an AccessibleState.SELECTED
273
// PropertyChangeEvent since this resulted in two
274
// identical events being fired since
275
// AbstractButton.fireItemStateChanged also fires the
276
// same event. This caused screen readers to speak the
277
// name of the item twice.
278

279         if (b != model.isSelected()) {
280             getModel().setSelected(b);
281         }
282     }
283
284     /**
285      * Returns true if the menu's popup window is visible.
286      *
287      * @return true if the menu is visible, else false
288      */

289     public boolean isPopupMenuVisible() {
290         ensurePopupMenuCreated();
291         return popupMenu.isVisible();
292     }
293
294     /**
295      * Sets the visibility of the menu's popup. If the menu is
296      * not enabled, this method will have no effect.
297      *
298      * @param b a boolean value -- true to make the menu visible,
299      * false to hide it
300      * @beaninfo
301      * description: The popup menu's visibility
302      * expert: true
303      * hidden: true
304      */

305     public void setPopupMenuVisible(boolean b) {
306     if (DEBUG) {
307         System.out.println("in JMenu.setPopupMenuVisible " + b);
308         // Thread.dumpStack();
309
}
310
311     boolean isVisible = isPopupMenuVisible();
312         if (b != isVisible && (isEnabled() || !b)) {
313             ensurePopupMenuCreated();
314             if ((b==true) && isShowing()) {
315                 // Set location of popupMenu (pulldown or pullright)
316
Point JavaDoc p = getCustomMenuLocation();
317         if (p == null) {
318             p = getPopupMenuOrigin();
319         }
320         getPopupMenu().show(this, p.x, p.y);
321             } else {
322                 getPopupMenu().setVisible(false);
323             }
324         }
325
326     }
327
328     /**
329      * Computes the origin for the <code>JMenu</code>'s popup menu.
330      * This method uses Look and Feel properties named
331      * <code>Menu.menuPopupOffsetX</code>,
332      * <code>Menu.menuPopupOffsetY</code>,
333      * <code>Menu.submenuPopupOffsetX</code>, and
334      * <code>Menu.submenuPopupOffsetY</code>
335      * to adjust the exact location of popup.
336      *
337      * @return a <code>Point</code> in the coordinate space of the
338      * menu which should be used as the origin
339      * of the <code>JMenu</code>'s popup menu
340      *
341      * @since 1.3
342      */

343     protected Point JavaDoc getPopupMenuOrigin() {
344     int x = 0;
345     int y = 0;
346     JPopupMenu JavaDoc pm = getPopupMenu();
347     // Figure out the sizes needed to caclulate the menu position
348
Dimension JavaDoc s = getSize();
349     Dimension JavaDoc pmSize = pm.getSize();
350     // For the first time the menu is popped up,
351
// the size has not yet been initiated
352
if (pmSize.width==0) {
353         pmSize = pm.getPreferredSize();
354     }
355     Point JavaDoc position = getLocationOnScreen();
356         Toolkit JavaDoc toolkit = Toolkit.getDefaultToolkit();
357         GraphicsConfiguration JavaDoc gc = getGraphicsConfiguration();
358         Rectangle JavaDoc screenBounds = new Rectangle JavaDoc(toolkit.getScreenSize());
359         GraphicsEnvironment JavaDoc ge =
360             GraphicsEnvironment.getLocalGraphicsEnvironment();
361         GraphicsDevice JavaDoc[] gd = ge.getScreenDevices();
362         for(int i = 0; i < gd.length; i++) {
363             if(gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
364                 GraphicsConfiguration JavaDoc dgc =
365                     gd[i].getDefaultConfiguration();
366                 if(dgc.getBounds().contains(position)) {
367                     gc = dgc;
368                     break;
369                 }
370             }
371         }
372
373
374         if (gc != null) {
375             screenBounds = gc.getBounds();
376             // take screen insets (e.g. taskbar) into account
377
Insets JavaDoc screenInsets = toolkit.getScreenInsets(gc);
378
379             screenBounds.width -=
380                         Math.abs(screenInsets.left + screenInsets.right);
381             screenBounds.height -=
382                         Math.abs(screenInsets.top + screenInsets.bottom);
383             position.x -= Math.abs(screenInsets.left);
384             position.y -= Math.abs(screenInsets.top);
385         }
386     
387     Container JavaDoc parent = getParent();
388     if (parent instanceof JPopupMenu JavaDoc) {
389         // We are a submenu (pull-right)
390
int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
391             int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
392
393             if( SwingUtilities.isLeftToRight(this) ) {
394                 // First determine x:
395
x = s.width + xOffset; // Prefer placement to the right
396
if (position.x + x + pmSize.width >= screenBounds.width
397                                                      + screenBounds.x &&
398                     // popup doesn't fit - place it wherever there's more room
399
screenBounds.width - s.width < 2*(position.x
400                                                     - screenBounds.x)) {
401
402                     x = 0 - xOffset - pmSize.width;
403                 }
404             } else {
405                 // First determine x:
406
x = 0 - xOffset - pmSize.width; // Prefer placement to the left
407
if (position.x + x < screenBounds.x &&
408                     // popup doesn't fit - place it wherever there's more room
409
screenBounds.width - s.width > 2*(position.x -
410                                                     screenBounds.x)) {
411
412                     x = s.width + xOffset;
413                 }
414             }
415             // Then the y:
416
y = yOffset; // Prefer dropping down
417
if (position.y + y + pmSize.height >= screenBounds.height
418                                                   + screenBounds.y &&
419                 // popup doesn't fit - place it wherever there's more room
420
screenBounds.height - s.height < 2*(position.y
421                                                   - screenBounds.y)) {
422
423                 y = s.height - yOffset - pmSize.height;
424             }
425     } else {
426         // We are a toplevel menu (pull-down)
427
int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
428             int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
429
430             if( SwingUtilities.isLeftToRight(this) ) {
431                 // First determine the x:
432
x = xOffset; // Extend to the right
433
if (position.x + x + pmSize.width >= screenBounds.width
434                                                      + screenBounds.x &&
435                     // popup doesn't fit - place it wherever there's more room
436
screenBounds.width - s.width < 2*(position.x
437                                                     - screenBounds.x)) {
438
439                     x = s.width - xOffset - pmSize.width;
440                 }
441             } else {
442                 // First determine the x:
443
x = s.width - xOffset - pmSize.width; // Extend to the left
444
if (position.x + x < screenBounds.x &&
445                     // popup doesn't fit - place it wherever there's more room
446
screenBounds.width - s.width > 2*(position.x
447                                                     - screenBounds.x)) {
448
449                     x = xOffset;
450                 }
451             }
452         // Then the y:
453
y = s.height + yOffset; // Prefer dropping down
454
if (position.y + y + pmSize.height >= screenBounds.height &&
455                 // popup doesn't fit - place it wherever there's more room
456
screenBounds.height - s.height < 2*(position.y
457                                                   - screenBounds.y)) {
458
459         y = 0 - yOffset - pmSize.height; // Otherwise drop 'up'
460
}
461     }
462     return new Point JavaDoc(x,y);
463     }
464
465
466     /**
467      * Returns the suggested delay, in milliseconds, before submenus
468      * are popped up or down.
469      * Each look and feel (L&F) may determine its own policy for
470      * observing the <code>delay</code> property.
471      * In most cases, the delay is not observed for top level menus
472      * or while dragging. The default for <code>delay</code> is 0.
473      * This method is a property of the look and feel code and is used
474      * to manage the idiosyncracies of the various UI implementations.
475      *
476      *
477      * @return the <code>delay</code> property
478      */

479     public int getDelay() {
480         return delay;
481     }
482     
483     /**
484      * Sets the suggested delay before the menu's <code>PopupMenu</code>
485      * is popped up or down. Each look and feel (L&F) may determine
486      * it's own policy for observing the delay property. In most cases,
487      * the delay is not observed for top level menus or while dragging.
488      * This method is a property of the look and feel code and is used
489      * to manage the idiosyncracies of the various UI implementations.
490      *
491      * @param d the number of milliseconds to delay
492      * @exception IllegalArgumentException if <code>d</code>
493      * is less than 0
494      * @beaninfo
495      * description: The delay between menu selection and making the popup menu visible
496      * expert: true
497      */

498     public void setDelay(int d) {
499         if (d < 0)
500             throw new IllegalArgumentException JavaDoc("Delay must be a positive integer");
501
502         delay = d;
503     }
504
505     /**
506      * The window-closing listener for the popup.
507      *
508      * @see WinListener
509      */

510     protected WinListener popupListener;
511
512     private void ensurePopupMenuCreated() {
513         if (popupMenu == null) {
514             final JMenu JavaDoc thisMenu = this;
515             this.popupMenu = new JPopupMenu JavaDoc();
516             popupMenu.setInvoker(this);
517             popupListener = createWinListener(popupMenu);
518         }
519     }
520
521     /*
522      * Return the customized location of the popup component.
523      */

524     private Point JavaDoc getCustomMenuLocation() {
525     return customMenuLocation;
526     }
527     
528     /**
529      * Sets the location of the popup component.
530      *
531      * @param x the x coordinate of the popup's new position
532      * @param y the y coordinate of the popup's new position
533      */

534     public void setMenuLocation(int x, int y) {
535     customMenuLocation = new Point JavaDoc(x, y);
536         if (popupMenu != null)
537         popupMenu.setLocation(x, y);
538     }
539
540     /**
541      * Appends a menu item to the end of this menu.
542      * Returns the menu item added.
543      *
544      * @param menuItem the <code>JMenuitem</code> to be added
545      * @return the <code>JMenuItem</code> added
546      */

547     public JMenuItem JavaDoc add(JMenuItem JavaDoc menuItem) {
548         AccessibleContext ac = menuItem.getAccessibleContext();
549         ac.setAccessibleParent(this);
550         ensurePopupMenuCreated();
551         return popupMenu.add(menuItem);
552     }
553
554     /**
555      * Appends a component to the end of this menu.
556      * Returns the component added.
557      *
558      * @param c the <code>Component</code> to add
559      * @return the <code>Component</code> added
560      */

561     public Component JavaDoc add(Component JavaDoc c) {
562     if (c instanceof JComponent JavaDoc) {
563         AccessibleContext ac = ((JComponent JavaDoc) c).getAccessibleContext();
564         if (ac != null) {
565         ac.setAccessibleParent(this);
566         }
567     }
568         ensurePopupMenuCreated();
569         popupMenu.add(c);
570         return c;
571     }
572
573     /**
574      * Adds the specified component to this container at the given
575      * position. If <code>index</code> equals -1, the component will
576      * be appended to the end.
577      * @param c the <code>Component</code> to add
578      * @param index the position at which to insert the component
579      * @return the <code>Component</code> added
580      * @see #remove
581      * @see java.awt.Container#add(Component, int)
582      */

583     public Component JavaDoc add(Component JavaDoc c, int index) {
584     if (c instanceof JComponent JavaDoc) {
585         AccessibleContext ac = ((JComponent JavaDoc) c).getAccessibleContext();
586         if (ac != null) {
587         ac.setAccessibleParent(this);
588         }
589     }
590         ensurePopupMenuCreated();
591         popupMenu.add(c, index);
592         return c;
593     }
594
595     /**
596      * Creates a new menu item with the specified text and appends
597      * it to the end of this menu.
598      *
599      * @param s the string for the menu item to be added
600      */

601     public JMenuItem JavaDoc add(String JavaDoc s) {
602         return add(new JMenuItem JavaDoc(s));
603     }
604
605     /**
606      * Creates a new menu item attached to the specified
607      * <code>Action</code> object and appends it to the end of this menu.
608      * As of 1.3, this is no longer the preferred method for adding
609      * <code>Actions</code> to
610      * a container. Instead it is recommended to configure a control with
611      * an action using <code>setAction</code>,
612      * and then add that control directly
613      * to the <code>Container</code>.
614      *
615      * @param a the <code>Action</code> for the menu item to be added
616      * @see Action
617      */

618     public JMenuItem JavaDoc add(Action JavaDoc a) {
619     JMenuItem JavaDoc mi = createActionComponent(a);
620         mi.setAction(a);
621         add(mi);
622         return mi;
623     }
624     
625     /**
626      * Factory method which creates the <code>JMenuItem</code> for
627      * <code>Action</code>s added to the <code>JMenu</code>.
628      * As of 1.3, this is no
629      * longer the preferred method. Instead it is recommended to configure
630      * a control with an action using <code>setAction</code>,
631      * and then adding that
632      * control directly to the <code>Container</code>.
633      *
634      * @param a the <code>Action</code> for the menu item to be added
635      * @return the new menu item
636      * @see Action
637      *
638      * @since 1.3
639      */

640     protected JMenuItem JavaDoc createActionComponent(Action JavaDoc a) {
641         JMenuItem JavaDoc mi = new JMenuItem JavaDoc((String JavaDoc)a.getValue(Action.NAME),
642                                      (Icon JavaDoc)a.getValue(Action.SMALL_ICON)){
643         protected PropertyChangeListener createActionPropertyChangeListener(Action JavaDoc a) {
644         PropertyChangeListener pcl = createActionChangeListener(this);
645         if (pcl == null) {
646             pcl = super.createActionPropertyChangeListener(a);
647         }
648         return pcl;
649         }
650     };
651         mi.setHorizontalTextPosition(JButton.TRAILING);
652         mi.setVerticalTextPosition(JButton.CENTER);
653         mi.setEnabled(a.isEnabled());
654     return mi;
655     }
656
657     /**
658      * Returns a properly configured <code>PropertyChangeListener</code>
659      * which updates the control as changes to the <code>Action</code> occur.
660      * As of 1.3, this is no longer the preferred method for adding
661      * <code>Action</code>s to a <code>Container</code>.
662      * Instead it is recommended to configure a control with
663      * an action using <code>setAction</code>, and then add that
664      * control directly
665      * to the <code>Container</code>.
666      */

667     protected PropertyChangeListener createActionChangeListener(JMenuItem JavaDoc b) {
668         return new ActionChangedListener(b);
669     }
670
671     private class ActionChangedListener implements PropertyChangeListener {
672         WeakReference JavaDoc menuItem;
673         
674         ActionChangedListener(JMenuItem JavaDoc mi) {
675             super();
676         setTarget(mi);
677         }
678         public void propertyChange(PropertyChangeEvent e) {
679             String JavaDoc propertyName = e.getPropertyName();
680             JMenuItem JavaDoc mi = (JMenuItem JavaDoc)getTarget();
681             if (mi == null) {
682                 Action JavaDoc action = (Action JavaDoc)e.getSource();
683                 action.removePropertyChangeListener(this);
684             } else {
685                 if (propertyName.equals(Action.NAME)) {
686                     String JavaDoc text = (String JavaDoc) e.getNewValue();
687                     mi.setText(text);
688                 } else if (propertyName.equals("enabled")) {
689                     Boolean JavaDoc enabledState = (Boolean JavaDoc) e.getNewValue();
690                     mi.setEnabled(enabledState.booleanValue());
691                 } else if (propertyName.equals(Action.SMALL_ICON)) {
692                     Icon JavaDoc icon = (Icon JavaDoc) e.getNewValue();
693                     mi.setIcon(icon);
694                     mi.invalidate();
695                     mi.repaint();
696                 } else if (propertyName.equals(Action.ACTION_COMMAND_KEY)) {
697                     mi.setActionCommand((String JavaDoc)e.getNewValue());
698                 }
699             }
700         }
701     public void setTarget(JMenuItem JavaDoc b) {
702         menuItem = new WeakReference JavaDoc(b);
703     }
704         public JMenuItem JavaDoc getTarget() {
705             return (JMenuItem JavaDoc)menuItem.get();
706         }
707     }
708
709     /**
710      * Appends a new separator to the end of the menu.
711      */

712     public void addSeparator()
713     {
714         ensurePopupMenuCreated();
715         popupMenu.addSeparator();
716     }
717
718     /**
719      * Inserts a new menu item with the specified text at a
720      * given position.
721      *
722      * @param s the text for the menu item to add
723      * @param pos an integer specifying the position at which to add the
724      * new menu item
725      * @exception IllegalArgumentException when the value of
726      * <code>pos</code> < 0
727      */

728     public void insert(String JavaDoc s, int pos) {
729         if (pos < 0) {
730             throw new IllegalArgumentException JavaDoc("index less than zero.");
731         }
732
733         ensurePopupMenuCreated();
734         popupMenu.insert(new JMenuItem JavaDoc(s), pos);
735     }
736
737     /**
738      * Inserts the specified <code>JMenuitem</code> at a given position.
739      *
740      * @param mi the <code>JMenuitem</code> to add
741      * @param pos an integer specifying the position at which to add the
742      * new <code>JMenuitem</code>
743      * @return the new menu item
744      * @exception IllegalArgumentException if the value of
745      * <code>pos</code> < 0
746      */

747     public JMenuItem JavaDoc insert(JMenuItem JavaDoc mi, int pos) {
748         if (pos < 0) {
749             throw new IllegalArgumentException JavaDoc("index less than zero.");
750         }
751         AccessibleContext ac = mi.getAccessibleContext();
752         ac.setAccessibleParent(this);
753         ensurePopupMenuCreated();
754         popupMenu.insert(mi, pos);
755         return mi;
756     }
757
758     /**
759      * Inserts a new menu item attached to the specified <code>Action</code>
760      * object at a given position.
761      *
762      * @param a the <code>Action</code> object for the menu item to add
763      * @param pos an integer specifying the position at which to add the
764      * new menu item
765      * @exception IllegalArgumentException if the value of
766      * <code>pos</code> < 0
767      */

768     public JMenuItem JavaDoc insert(Action JavaDoc a, int pos) {
769         if (pos < 0) {
770             throw new IllegalArgumentException JavaDoc("index less than zero.");
771         }
772
773         ensurePopupMenuCreated();
774         JMenuItem JavaDoc mi = new JMenuItem JavaDoc((String JavaDoc)a.getValue(Action.NAME),
775                      (Icon JavaDoc)a.getValue(Action.SMALL_ICON));
776         mi.setHorizontalTextPosition(JButton.TRAILING);
777         mi.setVerticalTextPosition(JButton.CENTER);
778         mi.setEnabled(a.isEnabled());
779         mi.setAction(a);
780         popupMenu.insert(mi, pos);
781         return mi;
782     }
783
784     /**
785      * Inserts a separator at the specified position.
786      *
787      * @param index an integer specifying the position at which to
788      * insert the menu separator
789      * @exception IllegalArgumentException if the value of
790      * <code>index</code> < 0
791      */

792     public void insertSeparator(int index) {
793         if (index < 0) {
794             throw new IllegalArgumentException JavaDoc("index less than zero.");
795         }
796
797         ensurePopupMenuCreated();
798         popupMenu.insert( new JPopupMenu.Separator JavaDoc(), index );
799     }
800
801     /**
802      * Returns the <code>JMenuItem</code> at the specified position.
803      * If the component at <code>pos</code> is not a menu item,
804      * <code>null</code> is returned.
805      * This method is included for AWT compatibility.
806      *
807      * @param pos an integer specifying the position
808      * @exception IllegalArgumentException if the value of
809      * <code>pos</code> < 0
810      * @return the menu item at the specified position; or <code>null</code>
811      * if the item as the specified position is not a menu item
812      */

813     public JMenuItem JavaDoc getItem(int pos) {
814         if (pos < 0) {
815             throw new IllegalArgumentException JavaDoc("index less than zero.");
816         }
817
818         Component JavaDoc c = getMenuComponent(pos);
819         if (c instanceof JMenuItem JavaDoc) {
820             JMenuItem JavaDoc mi = (JMenuItem JavaDoc) c;
821             return mi;
822         }
823
824         // 4173633
825
return null;
826     }
827
828     /**
829      * Returns the number of items on the menu, including separators.
830      * This method is included for AWT compatibility.
831      *
832      * @return an integer equal to the number of items on the menu
833      * @see #getMenuComponentCount
834      */

835     public int getItemCount() {
836         return getMenuComponentCount();
837     }
838
839     /**
840      * Returns true if the menu can be torn off. This method is not
841      * yet implemented.
842      *
843      * @return true if the menu can be torn off, else false
844      * @exception Error if invoked -- this method is not yet implemented
845      */

846     public boolean isTearOff() {
847         throw new Error JavaDoc("boolean isTearOff() {} not yet implemented");
848     }
849
850     /**
851      * Removes the specified menu item from this menu. If there is no
852      * popup menu, this method will have no effect.
853      *
854      * @param item the <code>JMenuItem</code> to be removed from the menu
855      */

856     public void remove(JMenuItem JavaDoc item) {
857         if (popupMenu != null)
858         popupMenu.remove(item);
859     }
860
861     /**
862      * Removes the menu item at the specified index from this menu.
863      *
864      * @param pos the position of the item to be removed
865      * @exception IllegalArgumentException if the value of
866      * <code>pos</code> < 0, or if <code>pos</code>
867      * is greater than the number of menu items
868      */

869     public void remove(int pos) {
870         if (pos < 0) {
871             throw new IllegalArgumentException JavaDoc("index less than zero.");
872         }
873         if (pos > getItemCount()) {
874             throw new IllegalArgumentException JavaDoc("index greater than the number of items.");
875         }
876         if (popupMenu != null)
877         popupMenu.remove(pos);
878     }
879
880     /**
881      * Removes the component <code>c</code> from this menu.
882      *
883      * @param c the component to be removed
884      */

885     public void remove(Component JavaDoc c) {
886         if (popupMenu != null)
887         popupMenu.remove(c);
888     }
889
890     /**
891      * Removes all menu items from this menu.
892      */

893     public void removeAll() {
894         if (popupMenu != null)
895         popupMenu.removeAll();
896     }
897
898     /**
899      * Returns the number of components on the menu.
900      *
901      * @return an integer containing the number of components on the menu
902      */

903     public int getMenuComponentCount() {
904         int componentCount = 0;
905         if (popupMenu != null)
906             componentCount = popupMenu.getComponentCount();
907         return componentCount;
908     }
909
910     /**
911      * Returns the component at position <code>n</code>.
912      *
913      * @param n the position of the component to be returned
914      * @return the component requested, or <code>null</code>
915      * if there is no popup menu
916      *
917      */

918     public Component JavaDoc getMenuComponent(int n) {
919         if (popupMenu != null)
920             return popupMenu.getComponent(n);
921         
922         return null;
923     }
924
925     /**
926      * Returns an array of <code>Component</code>s of the menu's
927      * subcomponents. Note that this returns all <code>Component</code>s
928      * in the popup menu, including separators.
929      *
930      * @return an array of <code>Component</code>s or an empty array
931      * if there is no popup menu
932      */

933     public Component JavaDoc[] getMenuComponents() {
934         if (popupMenu != null)
935             return popupMenu.getComponents();
936         
937         return new Component JavaDoc[0];
938     }
939
940     /**
941      * Returns true if the menu is a 'top-level menu', that is, if it is
942      * the direct child of a menubar.
943      *
944      * @return true if the menu is activated from the menu bar;
945      * false if the menu is activated from a menu item
946      * on another menu
947      */

948     public boolean isTopLevelMenu() {
949         if (getParent() instanceof JMenuBar JavaDoc)
950             return true;
951         
952         return false;
953     }
954
955     /**
956      * Returns true if the specified component exists in the
957      * submenu hierarchy.
958      *
959      * @param c the <code>Component</code> to be tested
960      * @return true if the <code>Component</code> exists, false otherwise
961      */

962     public boolean isMenuComponent(Component JavaDoc c) {
963         // Are we in the MenuItem part of the menu
964
if (c == this)
965             return true;
966         // Are we in the PopupMenu?
967
if (c instanceof JPopupMenu JavaDoc) {
968             JPopupMenu JavaDoc comp = (JPopupMenu JavaDoc) c;
969             if (comp == this.getPopupMenu())
970                 return true;
971         }
972         // Are we in a Component on the PopupMenu
973
int ncomponents = this.getMenuComponentCount();
974         Component JavaDoc[] component = this.getMenuComponents();
975         for (int i = 0 ; i < ncomponents ; i++) {
976             Component JavaDoc comp = component[i];
977             // Are we in the current component?
978
if (comp == c)
979                 return true;
980             // Hmmm, what about Non-menu containers?
981

982             // Recursive call for the Menu case
983
if (comp instanceof JMenu JavaDoc) {
984                 JMenu JavaDoc subMenu = (JMenu JavaDoc) comp;
985                 if (subMenu.isMenuComponent(c))
986                     return true;
987             }
988         }
989         return false;
990     }
991
992
993     /*
994      * Returns a point in the coordinate space of this menu's popupmenu
995      * which corresponds to the point <code>p</code> in the menu's
996      * coordinate space.
997      *
998      * @param p the point to be translated
999      * @return the point in the coordinate space of this menu's popupmenu
1000     */

1001    private Point JavaDoc translateToPopupMenu(Point JavaDoc p) {
1002        return translateToPopupMenu(p.x, p.y);
1003    }
1004
1005    /*
1006     * Returns a point in the coordinate space of this menu's popupmenu
1007     * which corresponds to the point (x,y) in the menu's coordinate space.
1008     *
1009     * @param x the x coordinate of the point to be translated
1010     * @param y the y coordinate of the point to be translated
1011     * @return the point in the coordinate space of this menu's popupmenu
1012     */

1013    private Point JavaDoc translateToPopupMenu(int x, int y) {
1014            int newX;
1015            int newY;
1016
1017            if (getParent() instanceof JPopupMenu JavaDoc) {
1018                newX = x - getSize().width;
1019                newY = y;
1020            } else {
1021                newX = x;
1022                newY = y - getSize().height;
1023            }
1024
1025            return new Point JavaDoc(newX, newY);
1026        }
1027
1028    /**
1029     * Returns the popupmenu associated with this menu. If there is
1030     * no popupmenu, it will create one.
1031     */

1032    public JPopupMenu JavaDoc getPopupMenu() {
1033        ensurePopupMenuCreated();
1034        return popupMenu;
1035    }
1036
1037    /**
1038     * Adds a listener for menu events.
1039     *
1040     * @param l the listener to be added
1041     */

1042    public void addMenuListener(MenuListener l) {
1043        listenerList.add(MenuListener.class, l);
1044    }
1045    
1046    /**
1047     * Removes a listener for menu events.
1048     *
1049     * @param l the listener to be removed
1050     */

1051    public void removeMenuListener(MenuListener l) {
1052        listenerList.remove(MenuListener.class, l);
1053    }
1054
1055    /**
1056     * Returns an array of all the <code>MenuListener</code>s added
1057     * to this JMenu with addMenuListener().
1058     *
1059     * @return all of the <code>MenuListener</code>s added or an empty
1060     * array if no listeners have been added
1061     * @since 1.4
1062     */

1063    public MenuListener[] getMenuListeners() {
1064        return (MenuListener[])listenerList.getListeners(MenuListener.class);
1065    }
1066
1067    /**
1068     * Notifies all listeners that have registered interest for
1069     * notification on this event type. The event instance
1070     * is created lazily.
1071     *
1072     * @exception Error if there is a <code>null</code> listener
1073     * @see EventListenerList
1074     */

1075    protected void fireMenuSelected() {
1076    if (DEBUG) {
1077        System.out.println("In JMenu.fireMenuSelected");
1078    }
1079        // Guaranteed to return a non-null array
1080
Object JavaDoc[] listeners = listenerList.getListenerList();
1081        // Process the listeners last to first, notifying
1082
// those that are interested in this event
1083
for (int i = listeners.length-2; i>=0; i-=2) {
1084            if (listeners[i]==MenuListener.class) {
1085                if (listeners[i+1]== null) {
1086            throw new Error JavaDoc(getText() +" has a NULL Listener!! " + i);
1087                } else {
1088                    // Lazily create the event:
1089
if (menuEvent == null)
1090                        menuEvent = new MenuEvent(this);
1091                    ((MenuListener)listeners[i+1]).menuSelected(menuEvent);
1092                }
1093            }
1094        }
1095    }
1096
1097    /**
1098     * Notifies all listeners that have registered interest for
1099     * notification on this event type. The event instance
1100     * is created lazily.
1101     *
1102     * @exception Error if there is a <code>null</code> listener
1103     * @see EventListenerList
1104     */

1105    protected void fireMenuDeselected() {
1106    if (DEBUG) {
1107        System.out.println("In JMenu.fireMenuDeselected");
1108    }
1109        // Guaranteed to return a non-null array
1110
Object JavaDoc[] listeners = listenerList.getListenerList();
1111        // Process the listeners last to first, notifying
1112
// those that are interested in this event
1113
for (int i = listeners.length-2; i>=0; i-=2) {
1114            if (listeners[i]==MenuListener.class) {
1115                if (listeners[i+1]== null) {
1116                    throw new Error JavaDoc(getText() +" has a NULL Listener!! " + i);
1117                } else {
1118                    // Lazily create the event:
1119
if (menuEvent == null)
1120                        menuEvent = new MenuEvent(this);
1121                    ((MenuListener)listeners[i+1]).menuDeselected(menuEvent);
1122                }
1123            }
1124        }
1125    }
1126
1127    /**
1128     * Notifies all listeners that have registered interest for
1129     * notification on this event type. The event instance
1130     * is created lazily.
1131     *
1132     * @exception Error if there is a <code>null</code> listener
1133     * @see EventListenerList
1134     */

1135    protected void fireMenuCanceled() {
1136    if (DEBUG) {
1137        System.out.println("In JMenu.fireMenuCanceled");
1138    }
1139        // Guaranteed to return a non-null array
1140
Object JavaDoc[] listeners = listenerList.getListenerList();
1141        // Process the listeners last to first, notifying
1142
// those that are interested in this event
1143
for (int i = listeners.length-2; i>=0; i-=2) {
1144            if (listeners[i]==MenuListener.class) {
1145                if (listeners[i+1]== null) {
1146                    throw new Error JavaDoc(getText() +" has a NULL Listener!! "
1147                                       + i);
1148                } else {
1149                    // Lazily create the event:
1150
if (menuEvent == null)
1151                        menuEvent = new MenuEvent(this);
1152                    ((MenuListener)listeners[i+1]).menuCanceled(menuEvent);
1153                }
1154            }
1155        }
1156    }
1157
1158    /**
1159     * Factory method which sets the <code>ActionEvent</code>
1160     * source's properties according to values from the
1161     * <code>Action</code> instance. The properties
1162     * which are set may differ for subclasses. By default,
1163     * the properties which get set are <code>Text, Icon, Enabled,
1164     * ToolTipText, ActionCommand</code>, and <code>Mnemonic</code>.
1165     *
1166     * @param a the <code>Action</code> from which to get the properties,
1167     * or <code>null</code>
1168     * @since 1.4
1169     * @see Action
1170     * @see #setAction
1171     */

1172    protected void configurePropertiesFromAction(Action JavaDoc a) {
1173        configurePropertiesFromAction(a, null);
1174    }
1175
1176    class MenuChangeListener implements ChangeListener, Serializable JavaDoc {
1177        boolean isSelected = false;
1178        public void stateChanged(ChangeEvent e) {
1179            ButtonModel JavaDoc model = (ButtonModel JavaDoc) e.getSource();
1180            boolean modelSelected = model.isSelected();
1181
1182            if (modelSelected != isSelected) {
1183                if (modelSelected == true) {
1184                    fireMenuSelected();
1185                } else {
1186                    fireMenuDeselected();
1187                }
1188                isSelected = modelSelected;
1189            }
1190        }
1191    }
1192
1193    private ChangeListener createMenuChangeListener() {
1194        return new MenuChangeListener();
1195    }
1196
1197
1198    /**
1199     * Creates a window-closing listener for the popup.
1200     *
1201     * @param p the <code>JPopupMenu</code>
1202     * @return the new window-closing listener
1203     *
1204     * @see WinListener
1205     */

1206    protected WinListener createWinListener(JPopupMenu JavaDoc p) {
1207        return new WinListener(p);
1208    }
1209
1210    /**
1211     * A listener class that watches for a popup window closing.
1212     * When the popup is closing, the listener deselects the menu.
1213     * <p>
1214     * <strong>Warning:</strong>
1215     * Serialized objects of this class will not be compatible with
1216     * future Swing releases. The current serialization support is
1217     * appropriate for short term storage or RMI between applications running
1218     * the same version of Swing. As of 1.4, support for long term storage
1219     * of all JavaBeans<sup><font size="-2">TM</font></sup>
1220     * has been added to the <code>java.beans</code> package.
1221     * Please see {@link java.beans.XMLEncoder}.
1222     */

1223    protected class WinListener extends WindowAdapter implements Serializable JavaDoc {
1224        JPopupMenu JavaDoc popupMenu;
1225        /**
1226         * Create the window listener for the specified popup.
1227         */

1228        public WinListener(JPopupMenu JavaDoc p) {
1229            this.popupMenu = p;
1230        }
1231        /**
1232         * Deselect the menu when the popup is closed from outside.
1233         */

1234        public void windowClosing(WindowEvent e) {
1235            setSelected(false);
1236        }
1237    }
1238
1239    /**
1240     * Messaged when the menubar selection changes to activate or
1241     * deactivate this menu.
1242     * Overrides <code>JMenuItem.menuSelectionChanged</code>.
1243     *
1244     * @param isIncluded true if this menu is active, false if
1245     * it is not
1246     */

1247    public void menuSelectionChanged(boolean isIncluded) {
1248    if (DEBUG) {
1249        System.out.println("In JMenu.menuSelectionChanged to " + isIncluded);
1250    }
1251        setSelected(isIncluded);
1252    }
1253
1254    /**
1255     * Returns an array of <code>MenuElement</code>s containing the submenu
1256     * for this menu component. If popup menu is <code>null</code> returns
1257     * an empty array. This method is required to conform to the
1258     * <code>MenuElement</code> interface. Note that since
1259     * <code>JSeparator</code>s do not conform to the <code>MenuElement</code>
1260     * interface, this array will only contain <code>JMenuItem</code>s.
1261     *
1262     * @return an array of <code>MenuElement</code> objects
1263     */

1264    public MenuElement JavaDoc[] getSubElements() {
1265        if(popupMenu == null)
1266            return new MenuElement JavaDoc[0];
1267        else {
1268            MenuElement JavaDoc result[] = new MenuElement JavaDoc[1];
1269            result[0] = popupMenu;
1270            return result;
1271        }
1272    }
1273
1274    
1275    // implements javax.swing.MenuElement
1276
/**
1277     * Returns the <code>java.awt.Component</code> used to
1278     * paint this <code>MenuElement</code>.
1279     * The returned component is used to convert events and detect if
1280     * an event is inside a menu component.
1281     */

1282    public Component JavaDoc getComponent() {
1283        return this;
1284    }
1285
1286
1287    /**
1288     * Sets the <code>ComponentOrientation</code> property of this menu
1289     * and all components contained within it. This includes all
1290     * components returned by {@link #getMenuComponents getMenuComponents}.
1291     *
1292     * @param o the new component orientation of this menu and
1293     * the components contained within it.
1294     * @exception NullPointerException if <code>orientation</code> is null.
1295     * @see java.awt.Component#setComponentOrientation
1296     * @see java.awt.Component#getComponentOrientation
1297     * @since 1.4
1298     */

1299    public void applyComponentOrientation(ComponentOrientation JavaDoc o) {
1300        super.applyComponentOrientation(o);
1301        
1302        if ( popupMenu != null ) {
1303            int ncomponents = getMenuComponentCount();
1304            for (int i = 0 ; i < ncomponents ; ++i) {
1305                getMenuComponent(i).applyComponentOrientation(o);
1306            }
1307            popupMenu.setComponentOrientation(o);
1308        }
1309    }
1310
1311    public void setComponentOrientation(ComponentOrientation JavaDoc o) {
1312        super.setComponentOrientation(o);
1313        if ( popupMenu != null ) {
1314            popupMenu.setComponentOrientation(o);
1315        }
1316    }
1317
1318    /**
1319     * <code>setAccelerator</code> is not defined for <code>JMenu</code>.
1320     * Use <code>setMnemonic</code> instead.
1321     * @param keyStroke the keystroke combination which will invoke
1322     * the <code>JMenuItem</code>'s actionlisteners
1323     * without navigating the menu hierarchy
1324     * @exception Error if invoked -- this method is not defined for JMenu.
1325     * Use <code>setMnemonic</code> instead
1326     *
1327     * @beaninfo
1328     * description: The keystroke combination which will invoke the JMenuItem's
1329     * actionlisteners without navigating the menu hierarchy
1330     * hidden: true
1331     */

1332    public void setAccelerator(KeyStroke JavaDoc keyStroke) {
1333        throw new Error JavaDoc("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
1334    }
1335
1336    /**
1337     * Processes key stroke events such as mnemonics and accelerators.
1338     *
1339     * @param evt the key event to be processed
1340     */

1341    protected void processKeyEvent(KeyEvent evt) {
1342    MenuSelectionManager.defaultManager().processKeyEvent(evt);
1343    if (evt.isConsumed())
1344        return;
1345
1346    super.processKeyEvent(evt);
1347    }
1348
1349    /**
1350     * Programmatically performs a "click". This overrides the method
1351     * <code>AbstractButton.doClick</code> in order to make the menu pop up.
1352     * @param pressTime indicates the number of milliseconds the
1353     * button was pressed for
1354     */

1355    public void doClick(int pressTime) {
1356    MenuElement JavaDoc me[] = buildMenuElementArray(this);
1357    MenuSelectionManager.defaultManager().setSelectedPath(me);
1358    }
1359
1360    /*
1361     * Build an array of menu elements - from <code>PopupMenu</code> to
1362     * the root <code>JMenuBar</code>.
1363     * @param leaf the leaf node from which to start building up the array
1364     * @return the array of menu items
1365     */

1366    private MenuElement JavaDoc[] buildMenuElementArray(JMenu JavaDoc leaf) {
1367    Vector elements = new Vector();
1368    Component JavaDoc current = leaf.getPopupMenu();
1369    JPopupMenu JavaDoc pop;
1370    JMenu JavaDoc menu;
1371    JMenuBar JavaDoc bar;
1372
1373    while (true) {
1374        if (current instanceof JPopupMenu JavaDoc) {
1375        pop = (JPopupMenu JavaDoc) current;
1376        elements.insertElementAt(pop, 0);
1377        current = pop.getInvoker();
1378        } else if (current instanceof JMenu JavaDoc) {
1379        menu = (JMenu JavaDoc) current;
1380        elements.insertElementAt(menu, 0);
1381        current = menu.getParent();
1382        } else if (current instanceof JMenuBar JavaDoc) {
1383        bar = (JMenuBar JavaDoc) current;
1384        elements.insertElementAt(bar, 0);
1385        MenuElement JavaDoc me[] = new MenuElement JavaDoc[elements.size()];
1386        elements.copyInto(me);
1387        return me;
1388        }
1389    }
1390    }
1391
1392
1393    /**
1394     * See <code>readObject</code> and <code>writeObject</code> in
1395     * <code>JComponent</code> for more
1396     * information about serialization in Swing.
1397     */

1398    private void writeObject(ObjectOutputStream JavaDoc s) throws IOException JavaDoc {
1399        s.defaultWriteObject();
1400        if (getUIClassID().equals(uiClassID)) {
1401            byte count = JComponent.getWriteObjCounter(this);
1402            JComponent.setWriteObjCounter(this, --count);
1403            if (count == 0 && ui != null) {
1404                ui.installUI(this);
1405            }
1406        }
1407    }
1408
1409
1410    /**
1411     * Returns a string representation of this <code>JMenu</code>. This
1412     * method is intended to be used only for debugging purposes, and the
1413     * content and format of the returned string may vary between
1414     * implementations. The returned string may be empty but may not
1415     * be <code>null</code>.
1416     *
1417     * @return a string representation of this JMenu.
1418     */

1419    protected String JavaDoc paramString() {
1420    return super.paramString();
1421    }
1422
1423
1424/////////////////
1425
// Accessibility support
1426
////////////////
1427

1428    /**
1429     * Gets the AccessibleContext associated with this JMenu.
1430     * For JMenus, the AccessibleContext takes the form of an
1431     * AccessibleJMenu.
1432     * A new AccessibleJMenu instance is created if necessary.
1433     *
1434     * @return an AccessibleJMenu that serves as the
1435     * AccessibleContext of this JMenu
1436     */

1437    public AccessibleContext getAccessibleContext() {
1438        if (accessibleContext == null) {
1439            accessibleContext = new AccessibleJMenu();
1440        }
1441        return accessibleContext;
1442    }
1443
1444    /**
1445     * This class implements accessibility support for the
1446     * <code>JMenu</code> class. It provides an implementation of the
1447     * Java Accessibility API appropriate to menu user-interface elements.
1448     * <p>
1449     * <strong>Warning:</strong>
1450     * Serialized objects of this class will not be compatible with
1451     * future Swing releases. The current serialization support is
1452     * appropriate for short term storage or RMI between applications running
1453     * the same version of Swing. As of 1.4, support for long term storage
1454     * of all JavaBeans<sup><font size="-2">TM</font></sup>
1455     * has been added to the <code>java.beans</code> package.
1456     * Please see {@link java.beans.XMLEncoder}.
1457     */

1458    protected class AccessibleJMenu extends AccessibleJMenuItem
1459    implements AccessibleSelection {
1460
1461        /**
1462         * Returns the number of accessible children in the object. If all
1463         * of the children of this object implement Accessible, than this
1464         * method should return the number of children of this object.
1465         *
1466         * @return the number of accessible children in the object.
1467         */

1468        public int getAccessibleChildrenCount() {
1469            Component JavaDoc[] children = getMenuComponents();
1470            int count = 0;
1471            for (int j = 0; j < children.length; j++) {
1472                if (children[j] instanceof Accessible) {
1473                    count++;
1474                }
1475            }
1476            return count;
1477        }
1478
1479        /**
1480         * Returns the nth Accessible child of the object.
1481         *
1482         * @param i zero-based index of child
1483         * @return the nth Accessible child of the object
1484         */

1485        public Accessible getAccessibleChild(int i) {
1486            Component JavaDoc[] children = getMenuComponents();
1487            int count = 0;
1488            for (int j = 0; j < children.length; j++) {
1489                if (children[j] instanceof Accessible) {
1490                    if (count == i) {
1491                        if (children[j] instanceof JComponent JavaDoc) {
1492                            // FIXME: [[[WDW - probably should set this when
1493
// the component is added to the menu. I tried
1494
// to do this in most cases, but the separators
1495
// added by addSeparator are hard to get to.]]]
1496
AccessibleContext ac = ((Accessible) children[j]).getAccessibleContext();
1497                            ac.setAccessibleParent(JMenu.this);
1498                        }
1499                        return (Accessible) children[j];
1500                    } else {
1501                        count++;
1502                    }
1503                }
1504            }
1505            return null;
1506        }
1507
1508        /**
1509         * Get the role of this object.
1510         *
1511         * @return an instance of AccessibleRole describing the role of the
1512         * object
1513         * @see AccessibleRole
1514         */

1515        public AccessibleRole getAccessibleRole() {
1516            return AccessibleRole.MENU;
1517        }
1518
1519        /**
1520         * Get the AccessibleSelection associated with this object. In the
1521         * implementation of the Java Accessibility API for this class,
1522     * return this object, which is responsible for implementing the
1523         * AccessibleSelection interface on behalf of itself.
1524     *
1525     * @return this object
1526         */

1527        public AccessibleSelection getAccessibleSelection() {
1528            return this;
1529        }
1530
1531        /**
1532         * Returns 1 if a sub-menu is currently selected in this menu.
1533         *
1534         * @return 1 if a menu is currently selected, else 0
1535         */

1536        public int getAccessibleSelectionCount() {
1537        MenuElement JavaDoc me[] =
1538        MenuSelectionManager.defaultManager().getSelectedPath();
1539        if (me != null) {
1540        for (int i = 0; i < me.length; i++) {
1541            if (me[i] == JMenu.this) { // this menu is selected
1542
if (i+1 < me.length) {
1543                return 1;
1544            }
1545            }
1546        }
1547        }
1548        return 0;
1549        }
1550
1551        /**
1552         * Returns the currently selected sub-menu if one is selected,
1553         * otherwise null (there can only be one selection, and it can
1554     * only be a sub-menu, as otherwise menu items don't remain
1555     * selected).
1556         */

1557        public Accessible getAccessibleSelection(int i) {
1558        // if i is a sub-menu & popped, return it
1559
if (i < 0 || i >= getItemCount()) {
1560        return null;
1561        }
1562        MenuElement JavaDoc me[] =
1563        MenuSelectionManager.defaultManager().getSelectedPath();
1564            if (me != null) {
1565        for (int j = 0; j < me.length; j++) {
1566            if (me[j] == JMenu.this) { // this menu is selected
1567
// so find the next JMenuItem in the MenuElement
1568
// array, and return it!
1569
while (++j < me.length) {
1570                if (me[j] instanceof JMenuItem JavaDoc) {
1571                return (Accessible) me[j];
1572                }
1573            }
1574            }
1575        }
1576        }
1577        return null;
1578        }
1579
1580        /**
1581         * Returns true if the current child of this object is selected
1582     * (that is, if this child is a popped-up submenu).
1583         *
1584         * @param i the zero-based index of the child in this Accessible
1585         * object.
1586         * @see AccessibleContext#getAccessibleChild
1587         */

1588        public boolean isAccessibleChildSelected(int i) {
1589        // if i is a sub-menu and is pop-ed up, return true, else false
1590
MenuElement JavaDoc me[] =
1591        MenuSelectionManager.defaultManager().getSelectedPath();
1592        if (me != null) {
1593        JMenuItem JavaDoc mi = JMenu.this.getItem(i);
1594        for (int j = 0; j < me.length; j++) {
1595            if (me[j] == mi) {
1596            return true;
1597            }
1598        }
1599        }
1600        return false;
1601        }
1602
1603
1604        /**
1605         * Selects the <code>i</code>th menu in the menu.
1606         * If that item is a submenu,
1607         * it will pop up in response. If a different item is already
1608     * popped up, this will force it to close. If this is a sub-menu
1609     * that is already popped up (selected), this method has no
1610     * effect.
1611         *
1612         * @param i the index of the item to be selected
1613         * @see #getAccessibleStateSet
1614         */

1615        public void addAccessibleSelection(int i) {
1616        if (i < 0 || i >= getItemCount()) {
1617        return;
1618        }
1619        JMenuItem JavaDoc mi = getItem(i);
1620        if (mi != null) {
1621        if (mi instanceof JMenu JavaDoc) {
1622            MenuElement JavaDoc me[] = buildMenuElementArray((JMenu JavaDoc) mi);
1623            MenuSelectionManager.defaultManager().setSelectedPath(me);
1624        } else {
1625            mi.doClick();
1626            MenuSelectionManager.defaultManager().setSelectedPath(null);
1627            }
1628        }
1629        }
1630
1631        /**
1632         * Removes the nth item from the selection. In general, menus
1633     * can only have one item within them selected at a time
1634     * (e.g. one sub-menu popped open).
1635         *
1636         * @param i the zero-based index of the selected item
1637         */

1638        public void removeAccessibleSelection(int i) {
1639        if (i < 0 || i >= getItemCount()) {
1640        return;
1641        }
1642        JMenuItem JavaDoc mi = getItem(i);
1643        if (mi != null && mi instanceof JMenu JavaDoc) {
1644        if (((JMenu JavaDoc) mi).isSelected()) {
1645            MenuElement JavaDoc old[] =
1646            MenuSelectionManager.defaultManager().getSelectedPath();
1647            MenuElement JavaDoc me[] = new MenuElement JavaDoc[old.length-2];
1648            for (int j = 0; j < old.length -2; j++) {
1649            me[j] = old[j];
1650            }
1651            MenuSelectionManager.defaultManager().setSelectedPath(me);
1652        }
1653            }
1654        }
1655
1656        /**
1657         * Clears the selection in the object, so that nothing in the
1658         * object is selected. This will close any open sub-menu.
1659         */

1660        public void clearAccessibleSelection() {
1661        // if this menu is selected, reset selection to only go
1662
// to this menu; else do nothing
1663
MenuElement JavaDoc old[] =
1664        MenuSelectionManager.defaultManager().getSelectedPath();
1665        if (old != null) {
1666        for (int j = 0; j < old.length; j++) {
1667            if (old[j] == JMenu.this) { // menu is in the selection!
1668
MenuElement JavaDoc me[] = new MenuElement JavaDoc[j+1];
1669            System.arraycopy(old, 0, me, 0, j);
1670            me[j] = JMenu.this.getPopupMenu();
1671            MenuSelectionManager.defaultManager().setSelectedPath(me);
1672            }
1673        }
1674            }
1675        }
1676
1677        /**
1678         * Normally causes every selected item in the object to be selected
1679         * if the object supports multiple selections. This method
1680         * makes no sense in a menu bar, and so does nothing.
1681         */

1682        public void selectAllAccessibleSelection() {
1683        }
1684    } // inner class AccessibleJMenu
1685

1686}
1687
1688
Popular Tags