blob: f2903fa29f92b23fa37f23cf8877a4d18ba25ade [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package java.awt;
26
27import java.io.IOException;
28import java.io.ObjectInputStream;
29import java.util.Vector;
30import java.util.Enumeration;
31import java.awt.peer.MenuPeer;
32import java.awt.event.KeyEvent;
33import javax.accessibility.*;
34
35/**
36 * A <code>Menu</code> object is a pull-down menu component
37 * that is deployed from a menu bar.
38 * <p>
39 * A menu can optionally be a <i>tear-off</i> menu. A tear-off menu
40 * can be opened and dragged away from its parent menu bar or menu.
41 * It remains on the screen after the mouse button has been released.
42 * The mechanism for tearing off a menu is platform dependent, since
43 * the look and feel of the tear-off menu is determined by its peer.
44 * On platforms that do not support tear-off menus, the tear-off
45 * property is ignored.
46 * <p>
47 * Each item in a menu must belong to the <code>MenuItem</code>
48 * class. It can be an instance of <code>MenuItem</code>, a submenu
49 * (an instance of <code>Menu</code>), or a check box (an instance of
50 * <code>CheckboxMenuItem</code>).
51 *
52 * @author Sami Shaio
53 * @see java.awt.MenuItem
54 * @see java.awt.CheckboxMenuItem
55 * @since JDK1.0
56 */
57public class Menu extends MenuItem implements MenuContainer, Accessible {
58
59 static {
60 /* ensure that the necessary native libraries are loaded */
61 Toolkit.loadLibraries();
62 if (!GraphicsEnvironment.isHeadless()) {
63 initIDs();
64 }
65 }
66
67 /**
68 * A vector of the items that will be part of the Menu.
69 *
70 * @serial
71 * @see #countItems()
72 */
73 Vector items = new Vector();
74
75 /**
76 * This field indicates whether the menu has the
77 * tear of property or not. It will be set to
78 * <code>true</code> if the menu has the tear off
79 * property and it will be set to <code>false</code>
80 * if it does not.
81 * A torn off menu can be deleted by a user when
82 * it is no longer needed.
83 *
84 * @serial
85 * @see #isTearOff()
86 */
87 boolean tearOff;
88
89 /**
90 * This field will be set to <code>true</code>
91 * if the Menu in question is actually a help
92 * menu. Otherwise it will be set to <code>
93 * false</code>.
94 *
95 * @serial
96 */
97 boolean isHelpMenu;
98
99 private static final String base = "menu";
100 private static int nameCounter = 0;
101
102 /*
103 * JDK 1.1 serialVersionUID
104 */
105 private static final long serialVersionUID = -8809584163345499784L;
106
107 /**
108 * Constructs a new menu with an empty label. This menu is not
109 * a tear-off menu.
110 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
111 * returns true.
112 * @see java.awt.GraphicsEnvironment#isHeadless
113 * @since JDK1.1
114 */
115 public Menu() throws HeadlessException {
116 this("", false);
117 }
118
119 /**
120 * Constructs a new menu with the specified label. This menu is not
121 * a tear-off menu.
122 * @param label the menu's label in the menu bar, or in
123 * another menu of which this menu is a submenu.
124 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
125 * returns true.
126 * @see java.awt.GraphicsEnvironment#isHeadless
127 */
128 public Menu(String label) throws HeadlessException {
129 this(label, false);
130 }
131
132 /**
133 * Constructs a new menu with the specified label,
134 * indicating whether the menu can be torn off.
135 * <p>
136 * Tear-off functionality may not be supported by all
137 * implementations of AWT. If a particular implementation doesn't
138 * support tear-off menus, this value is silently ignored.
139 * @param label the menu's label in the menu bar, or in
140 * another menu of which this menu is a submenu.
141 * @param tearOff if <code>true</code>, the menu
142 * is a tear-off menu.
143 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
144 * returns true.
145 * @see java.awt.GraphicsEnvironment#isHeadless
146 * @since JDK1.0.
147 */
148 public Menu(String label, boolean tearOff) throws HeadlessException {
149 super(label);
150 this.tearOff = tearOff;
151 }
152
153 /**
154 * Construct a name for this MenuComponent. Called by getName() when
155 * the name is null.
156 */
157 String constructComponentName() {
158 synchronized (Menu.class) {
159 return base + nameCounter++;
160 }
161 }
162
163 /**
164 * Creates the menu's peer. The peer allows us to modify the
165 * appearance of the menu without changing its functionality.
166 */
167 public void addNotify() {
168 synchronized (getTreeLock()) {
169 if (peer == null)
170 peer = Toolkit.getDefaultToolkit().createMenu(this);
171 int nitems = getItemCount();
172 for (int i = 0 ; i < nitems ; i++) {
173 MenuItem mi = getItem(i);
174 mi.parent = this;
175 mi.addNotify();
176 }
177 }
178 }
179
180 /**
181 * Removes the menu's peer. The peer allows us to modify the appearance
182 * of the menu without changing its functionality.
183 */
184 public void removeNotify() {
185 synchronized (getTreeLock()) {
186 int nitems = getItemCount();
187 for (int i = 0 ; i < nitems ; i++) {
188 getItem(i).removeNotify();
189 }
190 super.removeNotify();
191 }
192 }
193
194 /**
195 * Indicates whether this menu is a tear-off menu.
196 * <p>
197 * Tear-off functionality may not be supported by all
198 * implementations of AWT. If a particular implementation doesn't
199 * support tear-off menus, this value is silently ignored.
200 * @return <code>true</code> if this is a tear-off menu;
201 * <code>false</code> otherwise.
202 */
203 public boolean isTearOff() {
204 return tearOff;
205 }
206
207 /**
208 * Get the number of items in this menu.
209 * @return the number of items in this menu.
210 * @since JDK1.1
211 */
212 public int getItemCount() {
213 return countItems();
214 }
215
216 /**
217 * @deprecated As of JDK version 1.1,
218 * replaced by <code>getItemCount()</code>.
219 */
220 @Deprecated
221 public int countItems() {
222 return countItemsImpl();
223 }
224
225 /*
226 * This is called by the native code, so client code can't
227 * be called on the toolkit thread.
228 */
229 final int countItemsImpl() {
230 return items.size();
231 }
232
233 /**
234 * Gets the item located at the specified index of this menu.
235 * @param index the position of the item to be returned.
236 * @return the item located at the specified index.
237 */
238 public MenuItem getItem(int index) {
239 return getItemImpl(index);
240 }
241
242 /*
243 * This is called by the native code, so client code can't
244 * be called on the toolkit thread.
245 */
246 final MenuItem getItemImpl(int index) {
247 return (MenuItem)items.elementAt(index);
248 }
249
250 /**
251 * Adds the specified menu item to this menu. If the
252 * menu item has been part of another menu, removes it
253 * from that menu.
254 *
255 * @param mi the menu item to be added
256 * @return the menu item added
257 * @see java.awt.Menu#insert(java.lang.String, int)
258 * @see java.awt.Menu#insert(java.awt.MenuItem, int)
259 */
260 public MenuItem add(MenuItem mi) {
261 synchronized (getTreeLock()) {
262 if (mi.parent != null) {
263 mi.parent.remove(mi);
264 }
265 items.addElement(mi);
266 mi.parent = this;
267 MenuPeer peer = (MenuPeer)this.peer;
268 if (peer != null) {
269 mi.addNotify();
270 peer.addItem(mi);
271 }
272 return mi;
273 }
274 }
275
276 /**
277 * Adds an item with the specified label to this menu.
278 *
279 * @param label the text on the item
280 * @see java.awt.Menu#insert(java.lang.String, int)
281 * @see java.awt.Menu#insert(java.awt.MenuItem, int)
282 */
283 public void add(String label) {
284 add(new MenuItem(label));
285 }
286
287 /**
288 * Inserts a menu item into this menu
289 * at the specified position.
290 *
291 * @param menuitem the menu item to be inserted.
292 * @param index the position at which the menu
293 * item should be inserted.
294 * @see java.awt.Menu#add(java.lang.String)
295 * @see java.awt.Menu#add(java.awt.MenuItem)
296 * @exception IllegalArgumentException if the value of
297 * <code>index</code> is less than zero
298 * @since JDK1.1
299 */
300
301 public void insert(MenuItem menuitem, int index) {
302 synchronized (getTreeLock()) {
303 if (index < 0) {
304 throw new IllegalArgumentException("index less than zero.");
305 }
306
307 int nitems = getItemCount();
308 Vector tempItems = new Vector();
309
310 /* Remove the item at index, nitems-index times
311 storing them in a temporary vector in the
312 order they appear on the menu.
313 */
314 for (int i = index ; i < nitems; i++) {
315 tempItems.addElement(getItem(index));
316 remove(index);
317 }
318
319 add(menuitem);
320
321 /* Add the removed items back to the menu, they are
322 already in the correct order in the temp vector.
323 */
324 for (int i = 0; i < tempItems.size() ; i++) {
325 add((MenuItem)tempItems.elementAt(i));
326 }
327 }
328 }
329
330 /**
331 * Inserts a menu item with the specified label into this menu
332 * at the specified position. This is a convenience method for
333 * <code>insert(menuItem, index)</code>.
334 *
335 * @param label the text on the item
336 * @param index the position at which the menu item
337 * should be inserted
338 * @see java.awt.Menu#add(java.lang.String)
339 * @see java.awt.Menu#add(java.awt.MenuItem)
340 * @exception IllegalArgumentException if the value of
341 * <code>index</code> is less than zero
342 * @since JDK1.1
343 */
344
345 public void insert(String label, int index) {
346 insert(new MenuItem(label), index);
347 }
348
349 /**
350 * Adds a separator line, or a hypen, to the menu at the current position.
351 * @see java.awt.Menu#insertSeparator(int)
352 */
353 public void addSeparator() {
354 add("-");
355 }
356
357 /**
358 * Inserts a separator at the specified position.
359 * @param index the position at which the
360 * menu separator should be inserted.
361 * @exception IllegalArgumentException if the value of
362 * <code>index</code> is less than 0.
363 * @see java.awt.Menu#addSeparator
364 * @since JDK1.1
365 */
366
367 public void insertSeparator(int index) {
368 synchronized (getTreeLock()) {
369 if (index < 0) {
370 throw new IllegalArgumentException("index less than zero.");
371 }
372
373 int nitems = getItemCount();
374 Vector tempItems = new Vector();
375
376 /* Remove the item at index, nitems-index times
377 storing them in a temporary vector in the
378 order they appear on the menu.
379 */
380 for (int i = index ; i < nitems; i++) {
381 tempItems.addElement(getItem(index));
382 remove(index);
383 }
384
385 addSeparator();
386
387 /* Add the removed items back to the menu, they are
388 already in the correct order in the temp vector.
389 */
390 for (int i = 0; i < tempItems.size() ; i++) {
391 add((MenuItem)tempItems.elementAt(i));
392 }
393 }
394 }
395
396 /**
397 * Removes the menu item at the specified index from this menu.
398 * @param index the position of the item to be removed.
399 */
400 public void remove(int index) {
401 synchronized (getTreeLock()) {
402 MenuItem mi = getItem(index);
403 items.removeElementAt(index);
404 MenuPeer peer = (MenuPeer)this.peer;
405 if (peer != null) {
406 mi.removeNotify();
407 mi.parent = null;
408 peer.delItem(index);
409 }
410 }
411 }
412
413 /**
414 * Removes the specified menu item from this menu.
415 * @param item the item to be removed from the menu.
416 * If <code>item</code> is <code>null</code>
417 * or is not in this menu, this method does
418 * nothing.
419 */
420 public void remove(MenuComponent item) {
421 synchronized (getTreeLock()) {
422 int index = items.indexOf(item);
423 if (index >= 0) {
424 remove(index);
425 }
426 }
427 }
428
429 /**
430 * Removes all items from this menu.
431 * @since JDK1.0.
432 */
433 public void removeAll() {
434 synchronized (getTreeLock()) {
435 int nitems = getItemCount();
436 for (int i = nitems-1 ; i >= 0 ; i--) {
437 remove(i);
438 }
439 }
440 }
441
442 /*
443 * Post an ActionEvent to the target of the MenuPeer
444 * associated with the specified keyboard event (on
445 * keydown). Returns true if there is an associated
446 * keyboard event.
447 */
448 boolean handleShortcut(KeyEvent e) {
449 int nitems = getItemCount();
450 for (int i = 0 ; i < nitems ; i++) {
451 MenuItem mi = getItem(i);
452 if (mi.handleShortcut(e)) {
453 return true;
454 }
455 }
456 return false;
457 }
458
459 MenuItem getShortcutMenuItem(MenuShortcut s) {
460 int nitems = getItemCount();
461 for (int i = 0 ; i < nitems ; i++) {
462 MenuItem mi = getItem(i).getShortcutMenuItem(s);
463 if (mi != null) {
464 return mi;
465 }
466 }
467 return null;
468 }
469
470 synchronized Enumeration shortcuts() {
471 Vector shortcuts = new Vector();
472 int nitems = getItemCount();
473 for (int i = 0 ; i < nitems ; i++) {
474 MenuItem mi = getItem(i);
475 if (mi instanceof Menu) {
476 Enumeration e = ((Menu)mi).shortcuts();
477 while (e.hasMoreElements()) {
478 shortcuts.addElement(e.nextElement());
479 }
480 } else {
481 MenuShortcut ms = mi.getShortcut();
482 if (ms != null) {
483 shortcuts.addElement(ms);
484 }
485 }
486 }
487 return shortcuts.elements();
488 }
489
490 void deleteShortcut(MenuShortcut s) {
491 int nitems = getItemCount();
492 for (int i = 0 ; i < nitems ; i++) {
493 getItem(i).deleteShortcut(s);
494 }
495 }
496
497
498 /* Serialization support. A MenuContainer is responsible for
499 * restoring the parent fields of its children.
500 */
501
502 /**
503 * The menu serialized Data Version.
504 *
505 * @serial
506 */
507 private int menuSerializedDataVersion = 1;
508
509 /**
510 * Writes default serializable fields to stream.
511 *
512 * @param s the <code>ObjectOutputStream</code> to write
513 * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
514 * @see #readObject(ObjectInputStream)
515 */
516 private void writeObject(java.io.ObjectOutputStream s)
517 throws java.io.IOException
518 {
519 s.defaultWriteObject();
520 }
521
522 /**
523 * Reads the <code>ObjectInputStream</code>.
524 * Unrecognized keys or values will be ignored.
525 *
526 * @param s the <code>ObjectInputStream</code> to read
527 * @exception HeadlessException if
528 * <code>GraphicsEnvironment.isHeadless</code> returns
529 * <code>true</code>
530 * @see java.awt.GraphicsEnvironment#isHeadless
531 * @see #writeObject(ObjectOutputStream)
532 */
533 private void readObject(ObjectInputStream s)
534 throws IOException, ClassNotFoundException, HeadlessException
535 {
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=" + isHelpMenu;
555 return super.paramString() + str;
556 }
557
558 /**
559 * Initialize JNI field and method IDs
560 */
561 private static native void initIDs();
562
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 /*
606 * JDK 1.3 serialVersionUID
607 */
608 private static final long serialVersionUID = 5228160894980069094L;
609
610 /**
611 * Get the role of this object.
612 *
613 * @return an instance of AccessibleRole describing the role of the
614 * object
615 */
616 public AccessibleRole getAccessibleRole() {
617 return AccessibleRole.MENU;
618 }
619
620 } // class AccessibleAWTMenu
621
622}