|
1 /* |
|
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 */ |
|
25 package java.awt; |
|
26 |
|
27 import java.io.IOException; |
|
28 import java.io.ObjectInputStream; |
|
29 import java.util.Vector; |
|
30 import java.util.Enumeration; |
|
31 import java.awt.peer.MenuPeer; |
|
32 import java.awt.event.KeyEvent; |
|
33 import 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 */ |
|
57 public 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 } |