Frames | No Frames |
1: /* JMenu.java -- 2: Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.Component; 42: import java.awt.Point; 43: import java.awt.PopupMenu; 44: import java.awt.event.KeyEvent; 45: import java.awt.event.WindowAdapter; 46: import java.awt.event.WindowEvent; 47: import java.beans.PropertyChangeEvent; 48: import java.beans.PropertyChangeListener; 49: import java.io.Serializable; 50: import java.util.ArrayList; 51: import java.util.EventListener; 52: 53: import javax.accessibility.Accessible; 54: import javax.accessibility.AccessibleContext; 55: import javax.accessibility.AccessibleRole; 56: import javax.accessibility.AccessibleSelection; 57: import javax.swing.event.MenuEvent; 58: import javax.swing.event.MenuListener; 59: import javax.swing.plaf.MenuItemUI; 60: 61: /** 62: * This class represents a menu that can be added to a menu bar or 63: * can be a submenu in some other menu. When JMenu is selected it 64: * displays JPopupMenu containing its menu items. 65: * 66: * <p> 67: * JMenu's fires MenuEvents when this menu's selection changes. If this menu 68: * is selected, then fireMenuSelectedEvent() is invoked. In case when menu is 69: * deselected or cancelled, then fireMenuDeselectedEvent() or 70: * fireMenuCancelledEvent() is invoked, respectivelly. 71: * </p> 72: */ 73: public class JMenu extends JMenuItem implements Accessible, MenuElement 74: { 75: private static final long serialVersionUID = 4227225638931828014L; 76: 77: /** A Popup menu associated with this menu, which pops up when menu is selected */ 78: private JPopupMenu popupMenu = null; 79: 80: /** Whenever menu is selected or deselected the MenuEvent is fired to 81: menu's registered listeners. */ 82: private MenuEvent menuEvent = new MenuEvent(this); 83: 84: /*Amount of time, in milliseconds, that should pass before popupMenu 85: associated with this menu appears or disappers */ 86: private int delay; 87: 88: /* PopupListener */ 89: protected WinListener popupListener; 90: 91: /** Location at which popup menu associated with this menu will be 92: displayed */ 93: private Point menuLocation; 94: 95: /** 96: * Creates a new JMenu object. 97: */ 98: public JMenu() 99: { 100: super(); 101: setOpaque(false); 102: setDelay(200); 103: } 104: 105: /** 106: * Creates a new <code>JMenu</code> with the specified label. 107: * 108: * @param text label for this menu 109: */ 110: public JMenu(String text) 111: { 112: super(text); 113: popupMenu = new JPopupMenu(); 114: popupMenu.setInvoker(this); 115: setOpaque(false); 116: setDelay(200); 117: } 118: 119: /** 120: * Creates a new <code>JMenu</code> object. 121: * 122: * @param action Action that is used to create menu item tha will be 123: * added to the menu. 124: */ 125: public JMenu(Action action) 126: { 127: super(action); 128: createActionChangeListener(this); 129: popupMenu = new JPopupMenu(); 130: popupMenu.setInvoker(this); 131: setOpaque(false); 132: setDelay(200); 133: } 134: 135: /** 136: * Creates a new <code>JMenu</code> with specified label and an option 137: * for this menu to be tear-off menu. 138: * 139: * @param text label for this menu 140: * @param tearoff true if this menu should be tear-off and false otherwise 141: */ 142: public JMenu(String text, boolean tearoff) 143: { 144: // FIXME: tearoff not implemented 145: this(text); 146: setDelay(200); 147: } 148: 149: /** 150: * Adds specified menu item to this menu 151: * 152: * @param item Menu item to add to this menu 153: * 154: * @return Menu item that was added 155: */ 156: public JMenuItem add(JMenuItem item) 157: { 158: return getPopupMenu().add(item); 159: } 160: 161: /** 162: * Adds specified component to this menu. 163: * 164: * @param component Component to add to this menu 165: * 166: * @return Component that was added 167: */ 168: public Component add(Component component) 169: { 170: getPopupMenu().insert(component, -1); 171: return component; 172: } 173: 174: /** 175: * Adds specified component to this menu at the given index 176: * 177: * @param component Component to add 178: * @param index Position of this menu item in the menu 179: * 180: * @return Component that was added 181: */ 182: public Component add(Component component, int index) 183: { 184: return getPopupMenu().add(component, index); 185: } 186: 187: /** 188: * Adds JMenuItem constructed with the specified label to this menu 189: * 190: * @param text label for the menu item that will be added 191: * 192: * @return Menu Item that was added to this menu 193: */ 194: public JMenuItem add(String text) 195: { 196: return getPopupMenu().add(text); 197: } 198: 199: /** 200: * Adds JMenuItem constructed using properties from specified action. 201: * 202: * @param action action to construct the menu item with 203: * 204: * @return Menu Item that was added to this menu 205: */ 206: public JMenuItem add(Action action) 207: { 208: return getPopupMenu().add(action); 209: } 210: 211: /** 212: * Removes given menu item from this menu. Nothing happens if 213: * this menu doesn't contain specified menu item. 214: * 215: * @param item Menu Item which needs to be removed 216: */ 217: public void remove(JMenuItem item) 218: { 219: getPopupMenu().remove(item); 220: } 221: 222: /** 223: * Removes component at the specified index from this menu 224: * 225: * @param index Position of the component that needs to be removed in the menu 226: */ 227: public void remove(int index) 228: { 229: if (index < 0 || (index > 0 && getMenuComponentCount() == 0)) 230: throw new IllegalArgumentException(); 231: 232: if (getMenuComponentCount() > 0) 233: popupMenu.remove(index); 234: } 235: 236: /** 237: * Removes given component from this menu. 238: * 239: * @param component Component to remove 240: */ 241: public void remove(Component component) 242: { 243: int index = getPopupMenu().getComponentIndex(component); 244: if (index >= 0) 245: getPopupMenu().remove(index); 246: } 247: 248: /** 249: * Removes all menu items from the menu 250: */ 251: public void removeAll() 252: { 253: if (popupMenu != null) 254: popupMenu.removeAll(); 255: } 256: 257: /** 258: * Creates JMenuItem with the specified text and inserts it in the 259: * at the specified index 260: * 261: * @param text label for the new menu item 262: * @param index index at which to insert newly created menu item. 263: */ 264: public void insert(String text, int index) 265: { 266: this.insert(new JMenuItem(text), index); 267: } 268: 269: /** 270: * Creates JMenuItem with the specified text and inserts it in the 271: * at the specified index. IllegalArgumentException is thrown 272: * if index is less than 0 273: * 274: * @param item menu item to insert 275: * @param index index at which to insert menu item. 276: * @return Menu item that was added to the menu 277: */ 278: public JMenuItem insert(JMenuItem item, int index) 279: { 280: if (index < 0) 281: throw new IllegalArgumentException("index less than zero"); 282: 283: getPopupMenu().insert(item, index); 284: return item; 285: } 286: 287: /** 288: * Creates JMenuItem with the associated action and inserts it to the menu 289: * at the specified index. IllegalArgumentException is thrown 290: * if index is less than 0 291: * 292: * @param action Action for the new menu item 293: * @param index index at which to insert newly created menu item. 294: * @return Menu item that was added to the menu 295: */ 296: public JMenuItem insert(Action action, int index) 297: { 298: JMenuItem item = new JMenuItem(action); 299: this.insert(item, index); 300: 301: return item; 302: } 303: 304: /** 305: * This method sets this menuItem's UI to the UIManager's default for the 306: * current look and feel. 307: */ 308: public void updateUI() 309: { 310: setUI((MenuItemUI) UIManager.getUI(this)); 311: } 312: 313: /** 314: * This method returns a name to identify which look and feel class will be 315: * the UI delegate for the menu. 316: * 317: * @return The Look and Feel classID. "MenuUI" 318: */ 319: public String getUIClassID() 320: { 321: return "MenuUI"; 322: } 323: 324: /** 325: * Sets model for this menu. 326: * 327: * @param model model to set 328: */ 329: public void setModel(ButtonModel model) 330: { 331: super.setModel(model); 332: } 333: 334: /** 335: * Returns true if the menu is selected and false otherwise 336: * 337: * @return true if the menu is selected and false otherwise 338: */ 339: public boolean isSelected() 340: { 341: return super.isSelected(); 342: } 343: 344: /** 345: * A helper method to handle setSelected calls from both mouse events and 346: * direct calls to setSelected. Direct calls shouldn't expand the popup 347: * menu and should select the JMenu even if it is disabled. Mouse events 348: * only select the JMenu if it is enabled and should expand the popup menu 349: * associated with this JMenu. 350: * @param selected whether or not the JMenu was selected 351: * @param menuEnabled whether or not selecting the menu is "enabled". This 352: * is always true for direct calls, and is set to isEnabled() for mouse 353: * based calls. 354: * @param showMenu whether or not to show the popup menu 355: */ 356: private void setSelectedHelper(boolean selected, boolean menuEnabled, boolean showMenu) 357: { 358: // If menu is selected and enabled, activates the menu and 359: // displays associated popup. 360: if (selected && menuEnabled) 361: { 362: super.setArmed(true); 363: super.setSelected(true); 364: 365: // FIXME: The popup menu should be shown on the screen after certain 366: // number of seconds pass. The 'delay' property of this menu indicates 367: // this amount of seconds. 'delay' property is 0 by default. 368: if (isShowing()) 369: { 370: fireMenuSelected(); 371: 372: int x = 0; 373: int y = 0; 374: if (showMenu) 375: if (menuLocation == null) 376: { 377: // Calculate correct position of the popup. Note that location of the popup 378: // passed to show() should be relative to the popup's invoker 379: if (isTopLevelMenu()) 380: y = this.getHeight(); 381: else 382: x = this.getWidth(); 383: getPopupMenu().show(this, x, y); 384: } 385: else 386: { 387: getPopupMenu().show(this, menuLocation.x, menuLocation.y); 388: } 389: } 390: } 391: 392: else 393: { 394: super.setSelected(false); 395: super.setArmed(false); 396: fireMenuDeselected(); 397: getPopupMenu().setVisible(false); 398: } 399: } 400: 401: /** 402: * Changes this menu selected state if selected is true and false otherwise 403: * This method fires menuEvents to menu's registered listeners. 404: * 405: * @param selected true if the menu should be selected and false otherwise 406: */ 407: public void setSelected(boolean selected) 408: { 409: setSelectedHelper(selected, true, false); 410: } 411: 412: /** 413: * Checks if PopupMenu associated with this menu is visible 414: * 415: * @return true if the popup associated with this menu is currently visible 416: * on the screen and false otherwise. 417: */ 418: public boolean isPopupMenuVisible() 419: { 420: return getPopupMenu().isVisible(); 421: } 422: 423: /** 424: * Sets popup menu visibility 425: * 426: * @param popup true if popup should be visible and false otherwise 427: */ 428: public void setPopupMenuVisible(boolean popup) 429: { 430: if (getModel().isEnabled()) 431: getPopupMenu().setVisible(popup); 432: } 433: 434: /** 435: * Returns origin point of the popup menu 436: * 437: * @return Point containing 438: */ 439: protected Point getPopupMenuOrigin() 440: { 441: // if menu in the menu bar 442: if (isTopLevelMenu()) 443: return new Point(0, this.getHeight()); 444: 445: // if submenu 446: return new Point(this.getWidth(), 0); 447: } 448: 449: /** 450: * Returns delay property. 451: * 452: * @return delay property, indicating number of milliseconds before 453: * popup menu associated with the menu appears or disappears after 454: * menu was selected or deselected respectively 455: */ 456: public int getDelay() 457: { 458: return delay; 459: } 460: 461: /** 462: * Sets delay property for this menu. If given time for the delay 463: * property is negative, then IllegalArgumentException is thrown 464: * 465: * @param delay number of milliseconds before 466: * popup menu associated with the menu appears or disappears after 467: * menu was selected or deselected respectively 468: */ 469: public void setDelay(int delay) 470: { 471: if (delay < 0) 472: throw new IllegalArgumentException("delay less than 0"); 473: this.delay = delay; 474: } 475: 476: /** 477: * Sets location at which popup menu should be displayed 478: * The location given is relative to this menu item 479: * 480: * @param x x-coordinate of the menu location 481: * @param y y-coordinate of the menu location 482: */ 483: public void setMenuLocation(int x, int y) 484: { 485: menuLocation = new Point(x, y); 486: } 487: 488: /** 489: * Creates and returns JMenuItem associated with the given action 490: * 491: * @param action Action to use for creation of JMenuItem 492: * 493: * @return JMenuItem that was creted with given action 494: */ 495: protected JMenuItem createActionComponent(Action action) 496: { 497: return new JMenuItem(action); 498: } 499: 500: /** 501: * Creates ActionChangeListener to listen for PropertyChangeEvents occuring 502: * in the action that is associated with this menu 503: * 504: * @param item menu that contains action to listen to 505: * 506: * @return The PropertyChangeListener 507: */ 508: protected PropertyChangeListener createActionChangeListener(JMenuItem item) 509: { 510: return new ActionChangedListener(item); 511: } 512: 513: /** 514: * Adds separator to the end of the menu items in the menu. 515: */ 516: public void addSeparator() 517: { 518: getPopupMenu().addSeparator(); 519: } 520: 521: /** 522: * Inserts separator in the menu at the specified index. 523: * 524: * @param index Index at which separator should be inserted 525: */ 526: public void insertSeparator(int index) 527: { 528: if (index < 0) 529: throw new IllegalArgumentException("index less than 0"); 530: 531: getPopupMenu().insert(new JPopupMenu.Separator(), index); 532: } 533: 534: /** 535: * Returns menu item located at the specified index in the menu 536: * 537: * @param index Index at which to look for the menu item 538: * 539: * @return menu item located at the specified index in the menu 540: */ 541: public JMenuItem getItem(int index) 542: { 543: if (index < 0) 544: throw new IllegalArgumentException("index less than 0"); 545: 546: if (getItemCount() == 0) 547: return null; 548: 549: Component c = popupMenu.getComponentAtIndex(index); 550: 551: if (c instanceof JMenuItem) 552: return (JMenuItem) c; 553: else 554: return null; 555: } 556: 557: /** 558: * Returns number of items in the menu including separators. 559: * 560: * @return number of items in the menu 561: * 562: * @see #getMenuComponentCount() 563: */ 564: public int getItemCount() 565: { 566: return getMenuComponentCount(); 567: } 568: 569: /** 570: * Checks if this menu is a tear-off menu. 571: * 572: * @return true if this menu is a tear-off menu and false otherwise 573: */ 574: public boolean isTearOff() 575: { 576: // NOT YET IMPLEMENTED 577: throw new Error("The method isTearOff() has not yet been implemented."); 578: } 579: 580: /** 581: * Returns number of menu components in this menu 582: * 583: * @return number of menu components in this menu 584: */ 585: public int getMenuComponentCount() 586: { 587: return getPopupMenu().getComponentCount(); 588: } 589: 590: /** 591: * Returns menu component located at the givent index 592: * in the menu 593: * 594: * @param index index at which to get the menu component in the menu 595: * 596: * @return Menu Component located in the menu at the specified index 597: */ 598: public Component getMenuComponent(int index) 599: { 600: if (getPopupMenu() == null || getMenuComponentCount() == 0) 601: return null; 602: 603: return (Component) popupMenu.getComponentAtIndex(index); 604: } 605: 606: /** 607: * Return components belonging to this menu 608: * 609: * @return components belonging to this menu 610: */ 611: public Component[] getMenuComponents() 612: { 613: return getPopupMenu().getComponents(); 614: } 615: 616: /** 617: * Checks if this menu is a top level menu. The menu is top 618: * level menu if it is inside the menu bar. While if the menu 619: * inside some other menu, it is considered to be a pull-right menu. 620: * 621: * @return true if this menu is top level menu, and false otherwise 622: */ 623: public boolean isTopLevelMenu() 624: { 625: return getParent() instanceof JMenuBar; 626: } 627: 628: /** 629: * Checks if given component exists in this menu. The submenus of 630: * this menu are checked as well 631: * 632: * @param component Component to look for 633: * 634: * @return true if the given component exists in this menu, and false otherwise 635: */ 636: public boolean isMenuComponent(Component component) 637: { 638: return false; 639: } 640: 641: /** 642: * Returns popup menu associated with the menu. 643: * 644: * @return popup menu associated with the menu. 645: */ 646: public JPopupMenu getPopupMenu() 647: { 648: if (popupMenu == null) 649: { 650: popupMenu = new JPopupMenu(); 651: popupMenu.setInvoker(this); 652: } 653: return popupMenu; 654: } 655: 656: /** 657: * Adds MenuListener to the menu 658: * 659: * @param listener MenuListener to add 660: */ 661: public void addMenuListener(MenuListener listener) 662: { 663: listenerList.add(MenuListener.class, listener); 664: } 665: 666: /** 667: * Removes MenuListener from the menu 668: * 669: * @param listener MenuListener to remove 670: */ 671: public void removeMenuListener(MenuListener listener) 672: { 673: listenerList.remove(MenuListener.class, listener); 674: } 675: 676: /** 677: * Returns all registered <code>MenuListener</code> objects. 678: * 679: * @return an array of listeners 680: * 681: * @since 1.4 682: */ 683: public MenuListener[] getMenuListeners() 684: { 685: return (MenuListener[]) listenerList.getListeners(MenuListener.class); 686: } 687: 688: /** 689: * This method fires MenuEvents to all menu's MenuListeners. In this case 690: * menuSelected() method of MenuListeners is called to indicated that the menu 691: * was selected. 692: */ 693: protected void fireMenuSelected() 694: { 695: MenuListener[] listeners = getMenuListeners(); 696: 697: for (int index = 0; index < listeners.length; ++index) 698: listeners[index].menuSelected(menuEvent); 699: } 700: 701: /** 702: * This method fires MenuEvents to all menu's MenuListeners. In this case 703: * menuDeselected() method of MenuListeners is called to indicated that the menu 704: * was deselected. 705: */ 706: protected void fireMenuDeselected() 707: { 708: EventListener[] ll = listenerList.getListeners(MenuListener.class); 709: 710: for (int i = 0; i < ll.length; i++) 711: ((MenuListener) ll[i]).menuDeselected(menuEvent); 712: } 713: 714: /** 715: * This method fires MenuEvents to all menu's MenuListeners. In this case 716: * menuSelected() method of MenuListeners is called to indicated that the menu 717: * was cancelled. The menu is cancelled when it's popup menu is close without selection. 718: */ 719: protected void fireMenuCanceled() 720: { 721: EventListener[] ll = listenerList.getListeners(MenuListener.class); 722: 723: for (int i = 0; i < ll.length; i++) 724: ((MenuListener) ll[i]).menuCanceled(menuEvent); 725: } 726: 727: /** 728: * Creates WinListener that listens to the menu;s popup menu. 729: * 730: * @param popup JPopupMenu to listen to 731: * 732: * @return The WinListener 733: */ 734: protected WinListener createWinListener(JPopupMenu popup) 735: { 736: return new WinListener(popup); 737: } 738: 739: /** 740: * Method of the MenuElementInterface. It reacts to the selection 741: * changes in the menu. If this menu was selected, then it 742: * displayes popup menu associated with it and if this menu was 743: * deselected it hides the popup menu. 744: * 745: * @param changed true if the menu was selected and false otherwise 746: */ 747: public void menuSelectionChanged(boolean changed) 748: { 749: // if this menu selection is true, then activate this menu and 750: // display popup associated with this menu 751: setSelectedHelper(changed, isEnabled(), true); 752: } 753: 754: /** 755: * Method of MenuElement interface. Returns sub components of 756: * this menu. 757: * 758: * @return array containing popupMenu that is associated with this menu 759: */ 760: public MenuElement[] getSubElements() 761: { 762: return new MenuElement[] { popupMenu }; 763: } 764: 765: /** 766: * @return Returns reference to itself 767: */ 768: public Component getComponent() 769: { 770: return this; 771: } 772: 773: /** 774: * This method is overriden with empty implementation, s.t the 775: * accelerator couldn't be set for the menu. The mnemonic should 776: * be used for the menu instead. 777: * 778: * @param keystroke accelerator for this menu 779: */ 780: public void setAccelerator(KeyStroke keystroke) 781: { 782: throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead."); 783: } 784: 785: /** 786: * This method process KeyEvent occuring when the menu is visible 787: * 788: * @param event The KeyEvent 789: */ 790: protected void processKeyEvent(KeyEvent event) 791: { 792: MenuSelectionManager.defaultManager().processKeyEvent(event); 793: } 794: 795: /** 796: * Programatically performs click 797: * 798: * @param time Number of milliseconds for which this menu stays pressed 799: */ 800: public void doClick(int time) 801: { 802: getModel().setArmed(true); 803: getModel().setPressed(true); 804: try 805: { 806: java.lang.Thread.sleep(time); 807: } 808: catch (java.lang.InterruptedException e) 809: { 810: // probably harmless 811: } 812: 813: getModel().setPressed(false); 814: getModel().setArmed(false); 815: popupMenu.show(this, this.getWidth(), 0); 816: } 817: 818: /** 819: * A string that describes this JMenu. Normally only used 820: * for debugging. 821: * 822: * @return A string describing this JMenu 823: */ 824: protected String paramString() 825: { 826: return super.paramString(); 827: } 828: 829: public AccessibleContext getAccessibleContext() 830: { 831: if (accessibleContext == null) 832: accessibleContext = new AccessibleJMenu(); 833: 834: return accessibleContext; 835: } 836: 837: /** 838: * Implements support for assisitive technologies for <code>JMenu</code>. 839: */ 840: protected class AccessibleJMenu extends AccessibleJMenuItem 841: implements AccessibleSelection 842: { 843: private static final long serialVersionUID = -8131864021059524309L; 844: 845: protected AccessibleJMenu() 846: { 847: // Nothing to do here. 848: } 849: 850: /** 851: * Returns the number of accessible children of this object. 852: * 853: * @return the number of accessible children of this object 854: */ 855: public int getAccessibleChildrenCount() 856: { 857: Component[] children = getMenuComponents(); 858: int count = 0; 859: for (int i = 0; i < children.length; i++) 860: { 861: if (children[i] instanceof Accessible) 862: count++; 863: } 864: return count; 865: } 866: 867: /** 868: * Returns the accessible child with the specified <code>index</code>. 869: * 870: * @param index the index of the child to fetch 871: * 872: * @return the accessible child with the specified <code>index</code> 873: */ 874: public Accessible getAccessibleChild(int index) 875: { 876: Component[] children = getMenuComponents(); 877: int count = 0; 878: Accessible found = null; 879: for (int i = 0; i < children.length; i++) 880: { 881: if (children[i] instanceof Accessible) 882: { 883: if (count == index) 884: { 885: found = (Accessible) children[i]; 886: break; 887: } 888: count++; 889: } 890: } 891: return found; 892: } 893: 894: /** 895: * Returns the accessible selection of this object. AccessibleJMenus handle 896: * their selection themselves, so we always return <code>this</code> here. 897: * 898: * @return the accessible selection of this object 899: */ 900: public AccessibleSelection getAccessibleSelection() 901: { 902: return this; 903: } 904: 905: /** 906: * Returns the selected accessible child with the specified 907: * <code>index</code>. 908: * 909: * @param index the index of the accessible selected child to return 910: * 911: * @return the selected accessible child with the specified 912: * <code>index</code> 913: */ 914: public Accessible getAccessibleSelection(int index) 915: { 916: Accessible selected = null; 917: // Only one item can be selected, which must therefore have index == 0. 918: if (index == 0) 919: { 920: MenuSelectionManager msm = MenuSelectionManager.defaultManager(); 921: MenuElement[] me = msm.getSelectedPath(); 922: if (me != null) 923: { 924: for (int i = 0; i < me.length; i++) 925: { 926: if (me[i] == JMenu.this) 927: { 928: // This JMenu is selected, find and return the next 929: // JMenuItem in the path. 930: do 931: { 932: if (me[i] instanceof Accessible) 933: { 934: selected = (Accessible) me[i]; 935: break; 936: } 937: i++; 938: } while (i < me.length); 939: } 940: if (selected != null) 941: break; 942: } 943: } 944: } 945: return selected; 946: } 947: 948: /** 949: * Returns <code>true</code> if the accessible child with the specified 950: * index is selected, <code>false</code> otherwise. 951: * 952: * @param index the index of the accessible child to check 953: * 954: * @return <code>true</code> if the accessible child with the specified 955: * index is selected, <code>false</code> otherwise 956: */ 957: public boolean isAccessibleChildSelected(int index) 958: { 959: boolean selected = false; 960: MenuSelectionManager msm = MenuSelectionManager.defaultManager(); 961: MenuElement[] me = msm.getSelectedPath(); 962: if (me != null) 963: { 964: Accessible toBeFound = getAccessibleChild(index); 965: for (int i = 0; i < me.length; i++) 966: { 967: if (me[i] == toBeFound) 968: { 969: selected = true; 970: break; 971: } 972: } 973: } 974: return selected; 975: } 976: 977: /** 978: * Returns the accessible role of this object, which is 979: * {@link AccessibleRole#MENU} for the AccessibleJMenu. 980: * 981: * @return the accessible role of this object 982: */ 983: public AccessibleRole getAccessibleRole() 984: { 985: return AccessibleRole.MENU; 986: } 987: 988: /** 989: * Returns the number of selected accessible children. This will be 990: * <code>0</code> if no item is selected, or <code>1</code> if an item 991: * is selected. AccessibleJMenu can have maximum 1 selected item. 992: * 993: * @return the number of selected accessible children 994: */ 995: public int getAccessibleSelectionCount() 996: { 997: int count = 0; 998: MenuSelectionManager msm = MenuSelectionManager.defaultManager(); 999: MenuElement[] me = msm.getSelectedPath(); 1000: if (me != null) 1001: { 1002: for (int i = 0; i < me.length; i++) 1003: { 1004: if (me[i] == JMenu.this) 1005: { 1006: if (i + 1 < me.length) 1007: { 1008: count = 1; 1009: break; 1010: } 1011: } 1012: } 1013: } 1014: return count; 1015: } 1016: 1017: /** 1018: * Selects the accessible child with the specified index. 1019: * 1020: * @param index the index of the accessible child to select 1021: */ 1022: public void addAccessibleSelection(int index) 1023: { 1024: Accessible child = getAccessibleChild(index); 1025: if (child != null && child instanceof JMenuItem) 1026: { 1027: JMenuItem mi = (JMenuItem) child; 1028: MenuSelectionManager msm = MenuSelectionManager.defaultManager(); 1029: msm.setSelectedPath(createPath(JMenu.this)); 1030: } 1031: } 1032: 1033: /** 1034: * Removes the item with the specified index from the selection. 1035: * 1036: * @param index the index of the selected item to remove from the selection 1037: */ 1038: public void removeAccessibleSelection(int index) 1039: { 1040: Accessible child = getAccessibleChild(index); 1041: if (child != null) 1042: { 1043: MenuSelectionManager msm = MenuSelectionManager.defaultManager(); 1044: MenuElement[] oldSelection = msm.getSelectedPath(); 1045: for (int i = 0; i < oldSelection.length; i++) 1046: { 1047: if (oldSelection[i] == child) 1048: { 1049: // Found the specified child in the selection. Remove it 1050: // from the selection. 1051: MenuElement[] newSel = new MenuElement[i - 1]; 1052: System.arraycopy(oldSelection, 0, newSel, 0, i - 1); 1053: msm.setSelectedPath(newSel); 1054: break; 1055: } 1056: } 1057: } 1058: } 1059: 1060: /** 1061: * Removes all possibly selected accessible children of this object from 1062: * the selection. 1063: */ 1064: public void clearAccessibleSelection() 1065: { 1066: MenuSelectionManager msm = MenuSelectionManager.defaultManager(); 1067: MenuElement[] oldSelection = msm.getSelectedPath(); 1068: for (int i = 0; i < oldSelection.length; i++) 1069: { 1070: if (oldSelection[i] == JMenu.this) 1071: { 1072: // Found this menu in the selection. Remove all children from 1073: // the selection. 1074: MenuElement[] newSel = new MenuElement[i]; 1075: System.arraycopy(oldSelection, 0, newSel, 0, i); 1076: msm.setSelectedPath(newSel); 1077: break; 1078: } 1079: } 1080: } 1081: 1082: /** 1083: * AccessibleJMenu don't support multiple selection, so this method 1084: * does nothing. 1085: */ 1086: public void selectAllAccessibleSelection() 1087: { 1088: // Nothing to do here. 1089: } 1090: } 1091: 1092: protected class WinListener extends WindowAdapter implements Serializable 1093: { 1094: private static final long serialVersionUID = -6415815570638474823L; 1095: 1096: /** 1097: * Creates a new <code>WinListener</code>. 1098: * 1099: * @param popup the popup menu which is observed 1100: */ 1101: public WinListener(JPopupMenu popup) 1102: { 1103: // TODO: What should we do with the popup argument? 1104: } 1105: 1106: /** 1107: * Receives notification when the popup menu is closing and deselects 1108: * the menu. 1109: * 1110: * @param event the window event 1111: */ 1112: public void windowClosing(WindowEvent event) 1113: { 1114: setSelected(false); 1115: } 1116: } 1117: 1118: /** 1119: * This class listens to PropertyChangeEvents occuring in menu's action 1120: */ 1121: private class ActionChangedListener implements PropertyChangeListener 1122: { 1123: /** menu item associated with the action */ 1124: private JMenuItem menuItem; 1125: 1126: /** Creates new ActionChangedListener and adds it to menuItem's action */ 1127: public ActionChangedListener(JMenuItem menuItem) 1128: { 1129: this.menuItem = menuItem; 1130: 1131: Action a = menuItem.getAction(); 1132: if (a != null) 1133: a.addPropertyChangeListener(this); 1134: } 1135: 1136: /**This method is invoked when some change occures in menuItem's action*/ 1137: public void propertyChange(PropertyChangeEvent evt) 1138: { 1139: // FIXME: Need to implement 1140: } 1141: } 1142: 1143: /** 1144: * Creates an array to be feeded as argument to 1145: * {@link MenuSelectionManager#setSelectedPath(MenuElement[])} for the 1146: * specified element. This has the effect of selecting the specified element 1147: * and all its parents. 1148: * 1149: * @param leaf the leaf element for which to create the selected path 1150: * 1151: * @return the selected path array 1152: */ 1153: MenuElement[] createPath(JMenu leaf) 1154: { 1155: ArrayList path = new ArrayList(); 1156: MenuElement[] array = null; 1157: Component current = leaf.getPopupMenu(); 1158: while (true) 1159: { 1160: if (current instanceof JPopupMenu) 1161: { 1162: JPopupMenu popupMenu = (JPopupMenu) current; 1163: path.add(0, popupMenu); 1164: current = popupMenu.getInvoker(); 1165: } 1166: else if (current instanceof JMenu) 1167: { 1168: JMenu menu = (JMenu) current; 1169: path.add(0, menu); 1170: current = menu.getParent(); 1171: } 1172: else if (current instanceof JMenuBar) 1173: { 1174: JMenuBar menuBar = (JMenuBar) current; 1175: path.add(0, menuBar); 1176: array = new MenuElement[path.size()]; 1177: array = (MenuElement[]) path.toArray(array); 1178: break; 1179: } 1180: } 1181: return array; 1182: } 1183: }