001: /*
002: * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: package java.awt;
026:
027: import java.io.IOException;
028: import java.io.ObjectInputStream;
029: import java.util.Vector;
030: import java.util.Enumeration;
031: import java.awt.peer.MenuPeer;
032: import java.awt.event.KeyEvent;
033: import javax.accessibility.*;
034:
035: /**
036: * A <code>Menu</code> object is a pull-down menu component
037: * that is deployed from a menu bar.
038: * <p>
039: * A menu can optionally be a <i>tear-off</i> menu. A tear-off menu
040: * can be opened and dragged away from its parent menu bar or menu.
041: * It remains on the screen after the mouse button has been released.
042: * The mechanism for tearing off a menu is platform dependent, since
043: * the look and feel of the tear-off menu is determined by its peer.
044: * On platforms that do not support tear-off menus, the tear-off
045: * property is ignored.
046: * <p>
047: * Each item in a menu must belong to the <code>MenuItem</code>
048: * class. It can be an instance of <code>MenuItem</code>, a submenu
049: * (an instance of <code>Menu</code>), or a check box (an instance of
050: * <code>CheckboxMenuItem</code>).
051: *
052: * @version 1.85, 05/05/07
053: * @author Sami Shaio
054: * @see java.awt.MenuItem
055: * @see java.awt.CheckboxMenuItem
056: * @since JDK1.0
057: */
058: public class Menu extends MenuItem implements MenuContainer, Accessible {
059:
060: static {
061: /* ensure that the necessary native libraries are loaded */
062: Toolkit.loadLibraries();
063: if (!GraphicsEnvironment.isHeadless()) {
064: initIDs();
065: }
066: }
067:
068: /**
069: * A vector of the items that will be part of the Menu.
070: *
071: * @serial
072: * @see #countItems()
073: */
074: Vector items = new Vector();
075:
076: /**
077: * This field indicates whether the menu has the
078: * tear of property or not. It will be set to
079: * <code>true</code> if the menu has the tear off
080: * property and it will be set to <code>false</code>
081: * if it does not.
082: * A torn off menu can be deleted by a user when
083: * it is no longer needed.
084: *
085: * @serial
086: * @see #isTearOff()
087: */
088: boolean tearOff;
089:
090: /**
091: * This field will be set to <code>true</code>
092: * if the Menu in question is actually a help
093: * menu. Otherwise it will be set to <code>
094: * false</code>.
095: *
096: * @serial
097: */
098: boolean isHelpMenu;
099:
100: private static final String base = "menu";
101: private static int nameCounter = 0;
102:
103: /*
104: * JDK 1.1 serialVersionUID
105: */
106: private static final long serialVersionUID = -8809584163345499784L;
107:
108: /**
109: * Constructs a new menu with an empty label. This menu is not
110: * a tear-off menu.
111: * @exception HeadlessException if GraphicsEnvironment.isHeadless()
112: * returns true.
113: * @see java.awt.GraphicsEnvironment#isHeadless
114: * @since JDK1.1
115: */
116: public Menu() throws HeadlessException {
117: this ("", false);
118: }
119:
120: /**
121: * Constructs a new menu with the specified label. This menu is not
122: * a tear-off menu.
123: * @param label the menu's label in the menu bar, or in
124: * another menu of which this menu is a submenu.
125: * @exception HeadlessException if GraphicsEnvironment.isHeadless()
126: * returns true.
127: * @see java.awt.GraphicsEnvironment#isHeadless
128: */
129: public Menu(String label) throws HeadlessException {
130: this (label, false);
131: }
132:
133: /**
134: * Constructs a new menu with the specified label,
135: * indicating whether the menu can be torn off.
136: * <p>
137: * Tear-off functionality may not be supported by all
138: * implementations of AWT. If a particular implementation doesn't
139: * support tear-off menus, this value is silently ignored.
140: * @param label the menu's label in the menu bar, or in
141: * another menu of which this menu is a submenu.
142: * @param tearOff if <code>true</code>, the menu
143: * is a tear-off menu.
144: * @exception HeadlessException if GraphicsEnvironment.isHeadless()
145: * returns true.
146: * @see java.awt.GraphicsEnvironment#isHeadless
147: * @since JDK1.0.
148: */
149: public Menu(String label, boolean tearOff) throws HeadlessException {
150: super (label);
151: this .tearOff = tearOff;
152: }
153:
154: /**
155: * Construct a name for this MenuComponent. Called by getName() when
156: * the name is null.
157: */
158: String constructComponentName() {
159: synchronized (Menu.class) {
160: return base + nameCounter++;
161: }
162: }
163:
164: /**
165: * Creates the menu's peer. The peer allows us to modify the
166: * appearance of the menu without changing its functionality.
167: */
168: public void addNotify() {
169: synchronized (getTreeLock()) {
170: if (peer == null)
171: peer = Toolkit.getDefaultToolkit().createMenu(this );
172: int nitems = getItemCount();
173: for (int i = 0; i < nitems; i++) {
174: MenuItem mi = getItem(i);
175: mi.parent = this ;
176: mi.addNotify();
177: }
178: }
179: }
180:
181: /**
182: * Removes the menu's peer. The peer allows us to modify the appearance
183: * of the menu without changing its functionality.
184: */
185: public void removeNotify() {
186: synchronized (getTreeLock()) {
187: int nitems = getItemCount();
188: for (int i = 0; i < nitems; i++) {
189: getItem(i).removeNotify();
190: }
191: super .removeNotify();
192: }
193: }
194:
195: /**
196: * Indicates whether this menu is a tear-off menu.
197: * <p>
198: * Tear-off functionality may not be supported by all
199: * implementations of AWT. If a particular implementation doesn't
200: * support tear-off menus, this value is silently ignored.
201: * @return <code>true</code> if this is a tear-off menu;
202: * <code>false</code> otherwise.
203: */
204: public boolean isTearOff() {
205: return tearOff;
206: }
207:
208: /**
209: * Get the number of items in this menu.
210: * @return the number of items in this menu.
211: * @since JDK1.1
212: */
213: public int getItemCount() {
214: return countItems();
215: }
216:
217: /**
218: * @deprecated As of JDK version 1.1,
219: * replaced by <code>getItemCount()</code>.
220: */
221: @Deprecated
222: public int countItems() {
223: return countItemsImpl();
224: }
225:
226: /*
227: * This is called by the native code, so client code can't
228: * be called on the toolkit thread.
229: */
230: final int countItemsImpl() {
231: return items.size();
232: }
233:
234: /**
235: * Gets the item located at the specified index of this menu.
236: * @param index the position of the item to be returned.
237: * @return the item located at the specified index.
238: */
239: public MenuItem getItem(int index) {
240: return getItemImpl(index);
241: }
242:
243: /*
244: * This is called by the native code, so client code can't
245: * be called on the toolkit thread.
246: */
247: final MenuItem getItemImpl(int index) {
248: return (MenuItem) items.elementAt(index);
249: }
250:
251: /**
252: * Adds the specified menu item to this menu. If the
253: * menu item has been part of another menu, removes it
254: * from that menu.
255: *
256: * @param mi the menu item to be added
257: * @return the menu item added
258: * @see java.awt.Menu#insert(java.lang.String, int)
259: * @see java.awt.Menu#insert(java.awt.MenuItem, int)
260: */
261: public MenuItem add(MenuItem mi) {
262: synchronized (getTreeLock()) {
263: if (mi.parent != null) {
264: mi.parent.remove(mi);
265: }
266: items.addElement(mi);
267: mi.parent = this ;
268: MenuPeer peer = (MenuPeer) this .peer;
269: if (peer != null) {
270: mi.addNotify();
271: peer.addItem(mi);
272: }
273: return mi;
274: }
275: }
276:
277: /**
278: * Adds an item with the specified label to this menu.
279: *
280: * @param label the text on the item
281: * @see java.awt.Menu#insert(java.lang.String, int)
282: * @see java.awt.Menu#insert(java.awt.MenuItem, int)
283: */
284: public void add(String label) {
285: add(new MenuItem(label));
286: }
287:
288: /**
289: * Inserts a menu item into this menu
290: * at the specified position.
291: *
292: * @param menuitem the menu item to be inserted.
293: * @param index the position at which the menu
294: * item should be inserted.
295: * @see java.awt.Menu#add(java.lang.String)
296: * @see java.awt.Menu#add(java.awt.MenuItem)
297: * @exception IllegalArgumentException if the value of
298: * <code>index</code> is less than zero
299: * @since JDK1.1
300: */
301:
302: public void insert(MenuItem menuitem, int index) {
303: synchronized (getTreeLock()) {
304: if (index < 0) {
305: throw new IllegalArgumentException(
306: "index less than zero.");
307: }
308:
309: int nitems = getItemCount();
310: Vector tempItems = new Vector();
311:
312: /* Remove the item at index, nitems-index times
313: storing them in a temporary vector in the
314: order they appear on the menu.
315: */
316: for (int i = index; i < nitems; i++) {
317: tempItems.addElement(getItem(index));
318: remove(index);
319: }
320:
321: add(menuitem);
322:
323: /* Add the removed items back to the menu, they are
324: already in the correct order in the temp vector.
325: */
326: for (int i = 0; i < tempItems.size(); i++) {
327: add((MenuItem) tempItems.elementAt(i));
328: }
329: }
330: }
331:
332: /**
333: * Inserts a menu item with the specified label into this menu
334: * at the specified position. This is a convenience method for
335: * <code>insert(menuItem, index)</code>.
336: *
337: * @param label the text on the item
338: * @param index the position at which the menu item
339: * should be inserted
340: * @see java.awt.Menu#add(java.lang.String)
341: * @see java.awt.Menu#add(java.awt.MenuItem)
342: * @exception IllegalArgumentException if the value of
343: * <code>index</code> is less than zero
344: * @since JDK1.1
345: */
346:
347: public void insert(String label, int index) {
348: insert(new MenuItem(label), index);
349: }
350:
351: /**
352: * Adds a separator line, or a hypen, to the menu at the current position.
353: * @see java.awt.Menu#insertSeparator(int)
354: */
355: public void addSeparator() {
356: add("-");
357: }
358:
359: /**
360: * Inserts a separator at the specified position.
361: * @param index the position at which the
362: * menu separator should be inserted.
363: * @exception IllegalArgumentException if the value of
364: * <code>index</code> is less than 0.
365: * @see java.awt.Menu#addSeparator
366: * @since JDK1.1
367: */
368:
369: public void insertSeparator(int index) {
370: synchronized (getTreeLock()) {
371: if (index < 0) {
372: throw new IllegalArgumentException(
373: "index less than zero.");
374: }
375:
376: int nitems = getItemCount();
377: Vector tempItems = new Vector();
378:
379: /* Remove the item at index, nitems-index times
380: storing them in a temporary vector in the
381: order they appear on the menu.
382: */
383: for (int i = index; i < nitems; i++) {
384: tempItems.addElement(getItem(index));
385: remove(index);
386: }
387:
388: addSeparator();
389:
390: /* Add the removed items back to the menu, they are
391: already in the correct order in the temp vector.
392: */
393: for (int i = 0; i < tempItems.size(); i++) {
394: add((MenuItem) tempItems.elementAt(i));
395: }
396: }
397: }
398:
399: /**
400: * Removes the menu item at the specified index from this menu.
401: * @param index the position of the item to be removed.
402: */
403: public void remove(int index) {
404: synchronized (getTreeLock()) {
405: MenuItem mi = getItem(index);
406: items.removeElementAt(index);
407: MenuPeer peer = (MenuPeer) this .peer;
408: if (peer != null) {
409: mi.removeNotify();
410: mi.parent = null;
411: peer.delItem(index);
412: }
413: }
414: }
415:
416: /**
417: * Removes the specified menu item from this menu.
418: * @param item the item to be removed from the menu.
419: * If <code>item</code> is <code>null</code>
420: * or is not in this menu, this method does
421: * nothing.
422: */
423: public void remove(MenuComponent item) {
424: synchronized (getTreeLock()) {
425: int index = items.indexOf(item);
426: if (index >= 0) {
427: remove(index);
428: }
429: }
430: }
431:
432: /**
433: * Removes all items from this menu.
434: * @since JDK1.0.
435: */
436: public void removeAll() {
437: synchronized (getTreeLock()) {
438: int nitems = getItemCount();
439: for (int i = nitems - 1; i >= 0; i--) {
440: remove(i);
441: }
442: }
443: }
444:
445: /*
446: * Post an ActionEvent to the target of the MenuPeer
447: * associated with the specified keyboard event (on
448: * keydown). Returns true if there is an associated
449: * keyboard event.
450: */
451: boolean handleShortcut(KeyEvent e) {
452: int nitems = getItemCount();
453: for (int i = 0; i < nitems; i++) {
454: MenuItem mi = getItem(i);
455: if (mi.handleShortcut(e)) {
456: return true;
457: }
458: }
459: return false;
460: }
461:
462: MenuItem getShortcutMenuItem(MenuShortcut s) {
463: int nitems = getItemCount();
464: for (int i = 0; i < nitems; i++) {
465: MenuItem mi = getItem(i).getShortcutMenuItem(s);
466: if (mi != null) {
467: return mi;
468: }
469: }
470: return null;
471: }
472:
473: synchronized Enumeration shortcuts() {
474: Vector shortcuts = new Vector();
475: int nitems = getItemCount();
476: for (int i = 0; i < nitems; i++) {
477: MenuItem mi = getItem(i);
478: if (mi instanceof Menu) {
479: Enumeration e = ((Menu) mi).shortcuts();
480: while (e.hasMoreElements()) {
481: shortcuts.addElement(e.nextElement());
482: }
483: } else {
484: MenuShortcut ms = mi.getShortcut();
485: if (ms != null) {
486: shortcuts.addElement(ms);
487: }
488: }
489: }
490: return shortcuts.elements();
491: }
492:
493: void deleteShortcut(MenuShortcut s) {
494: int nitems = getItemCount();
495: for (int i = 0; i < nitems; i++) {
496: getItem(i).deleteShortcut(s);
497: }
498: }
499:
500: /* Serialization support. A MenuContainer is responsible for
501: * restoring the parent fields of its children.
502: */
503:
504: /**
505: * The menu serialized Data Version.
506: *
507: * @serial
508: */
509: private int menuSerializedDataVersion = 1;
510:
511: /**
512: * Writes default serializable fields to stream.
513: *
514: * @param s the <code>ObjectOutputStream</code> to write
515: * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
516: * @see #readObject(ObjectInputStream)
517: */
518: private void writeObject(java.io.ObjectOutputStream s)
519: throws java.io.IOException {
520: s.defaultWriteObject();
521: }
522:
523: /**
524: * Reads the <code>ObjectInputStream</code>.
525: * Unrecognized keys or values will be ignored.
526: *
527: * @param s the <code>ObjectInputStream</code> to read
528: * @exception HeadlessException if
529: * <code>GraphicsEnvironment.isHeadless</code> returns
530: * <code>true</code>
531: * @see java.awt.GraphicsEnvironment#isHeadless
532: * @see #writeObject(ObjectOutputStream)
533: */
534: private void readObject(ObjectInputStream s) throws IOException,
535: ClassNotFoundException, HeadlessException {
536: // HeadlessException will be thrown from MenuComponent's readObject
537: s.defaultReadObject();
538: for (int i = 0; i < items.size(); i++) {
539: MenuItem item = (MenuItem) items.elementAt(i);
540: item.parent = this ;
541: }
542: }
543:
544: /**
545: * Returns a string representing the state of this <code>Menu</code>.
546: * This method is intended to be used only for debugging purposes, and the
547: * content and format of the returned string may vary between
548: * implementations. The returned string may be empty but may not be
549: * <code>null</code>.
550: *
551: * @return the parameter string of this menu
552: */
553: public String paramString() {
554: String str = ",tearOff=" + tearOff + ",isHelpMenu="
555: + isHelpMenu;
556: return super .paramString() + str;
557: }
558:
559: /**
560: * Initialize JNI field and method IDs
561: */
562: private static native void initIDs();
563:
564: /////////////////
565: // Accessibility support
566: ////////////////
567:
568: /**
569: * Gets the AccessibleContext associated with this Menu.
570: * For menus, the AccessibleContext takes the form of an
571: * AccessibleAWTMenu.
572: * A new AccessibleAWTMenu instance is created if necessary.
573: *
574: * @return an AccessibleAWTMenu that serves as the
575: * AccessibleContext of this Menu
576: * @since 1.3
577: */
578: public AccessibleContext getAccessibleContext() {
579: if (accessibleContext == null) {
580: accessibleContext = new AccessibleAWTMenu();
581: }
582: return accessibleContext;
583: }
584:
585: /**
586: * Defined in MenuComponent. Overridden here.
587: */
588: int getAccessibleChildIndex(MenuComponent child) {
589: return items.indexOf(child);
590: }
591:
592: /**
593: * Inner class of Menu used to provide default support for
594: * accessibility. This class is not meant to be used directly by
595: * application developers, but is instead meant only to be
596: * subclassed by menu component developers.
597: * <p>
598: * This class implements accessibility support for the
599: * <code>Menu</code> class. It provides an implementation of the
600: * Java Accessibility API appropriate to menu user-interface elements.
601: * @since 1.3
602: */
603: protected class AccessibleAWTMenu extends AccessibleAWTMenuItem {
604: /*
605: * JDK 1.3 serialVersionUID
606: */
607: private static final long serialVersionUID = 5228160894980069094L;
608:
609: /**
610: * Get the role of this object.
611: *
612: * @return an instance of AccessibleRole describing the role of the
613: * object
614: */
615: public AccessibleRole getAccessibleRole() {
616: return AccessibleRole.MENU;
617: }
618:
619: } // class AccessibleAWTMenu
620:
621: }
|