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 }
|