GNU Classpath (0.17) | ||
Frames | No Frames |
1: /* JMenu.java -- 2: Copyright (C) 2002, 2004, 2005 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.event.KeyEvent; 44: import java.awt.event.WindowAdapter; 45: import java.awt.event.WindowEvent; 46: import java.beans.PropertyChangeEvent; 47: import java.beans.PropertyChangeListener; 48: import java.io.IOException; 49: import java.io.ObjectOutputStream; 50: import java.io.Serializable; 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 = new JPopupMenu(); 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: } 102: 103: /** 104: * Creates a new <code>JMenu</code> with the specified label. 105: * 106: * @param text label for this menu 107: */ 108: public JMenu(String text) 109: { 110: super(text); 111: popupMenu.setInvoker(this); 112: } 113: 114: /** 115: * Creates a new <code>JMenu</code> object. 116: * 117: * @param action Action that is used to create menu item tha will be 118: * added to the menu. 119: */ 120: public JMenu(Action action) 121: { 122: super(action); 123: createActionChangeListener(this); 124: popupMenu.setInvoker(this); 125: } 126: 127: /** 128: * Creates a new <code>JMenu</code> with specified label and an option 129: * for this menu to be tear-off menu. 130: * 131: * @param text label for this menu 132: * @param tearoff true if this menu should be tear-off and false otherwise 133: */ 134: public JMenu(String text, boolean tearoff) 135: { 136: // FIXME: tearoff not implemented 137: this(text); 138: } 139: 140: private void writeObject(ObjectOutputStream stream) throws IOException 141: { 142: } 143: 144: /** 145: * Adds specified menu item to this menu 146: * 147: * @param item Menu item to add to this menu 148: * 149: * @return Menu item that was added 150: */ 151: public JMenuItem add(JMenuItem item) 152: { 153: return popupMenu.add(item); 154: } 155: 156: /** 157: * Adds specified component to this menu. 158: * 159: * @param component Component to add to this menu 160: * 161: * @return Component that was added 162: */ 163: public Component add(Component component) 164: { 165: popupMenu.insert(component, -1); 166: return component; 167: } 168: 169: /** 170: * Adds specified component to this menu at the given index 171: * 172: * @param component Component to add 173: * @param index Position of this menu item in the menu 174: * 175: * @return Component that was added 176: */ 177: public Component add(Component component, int index) 178: { 179: return popupMenu.add(component, index); 180: } 181: 182: /** 183: * Adds JMenuItem constructed with the specified label to this menu 184: * 185: * @param text label for the menu item that will be added 186: * 187: * @return Menu Item that was added to this menu 188: */ 189: public JMenuItem add(String text) 190: { 191: return popupMenu.add(text); 192: } 193: 194: /** 195: * Adds JMenuItem constructed using properties from specified action. 196: * 197: * @param action action to construct the menu item with 198: * 199: * @return Menu Item that was added to this menu 200: */ 201: public JMenuItem add(Action action) 202: { 203: return popupMenu.add(action); 204: } 205: 206: /** 207: * Removes given menu item from this menu. Nothing happens if 208: * this menu doesn't contain specified menu item. 209: * 210: * @param item Menu Item which needs to be removed 211: */ 212: public void remove(JMenuItem item) 213: { 214: popupMenu.remove(item); 215: } 216: 217: /** 218: * Removes component at the specified index from this menu 219: * 220: * @param index Position of the component that needs to be removed in the menu 221: */ 222: public void remove(int index) 223: { 224: popupMenu.remove(index); 225: } 226: 227: /** 228: * Removes given component from this menu. 229: * 230: * @param component Component to remove 231: */ 232: public void remove(Component component) 233: { 234: int index = popupMenu.getComponentIndex(component); 235: popupMenu.remove(index); 236: } 237: 238: /** 239: * Removes all menu items from the menu 240: */ 241: public void removeAll() 242: { 243: popupMenu.removeAll(); 244: } 245: 246: /** 247: * Creates JMenuItem with the specified text and inserts it in the 248: * at the specified index 249: * 250: * @param text label for the new menu item 251: * @param index index at which to insert newly created menu item. 252: */ 253: public void insert(String text, int index) 254: { 255: this.insert(new JMenuItem(text), index); 256: } 257: 258: /** 259: * Creates JMenuItem with the specified text and inserts it in the 260: * at the specified index. IllegalArgumentException is thrown 261: * if index is less than 0 262: * 263: * @param item menu item to insert 264: * @param index index at which to insert menu item. 265: * @return Menu item that was added to the menu 266: */ 267: public JMenuItem insert(JMenuItem item, int index) 268: { 269: if (index < 0) 270: throw new IllegalArgumentException("index less than zero"); 271: 272: popupMenu.insert(item, index); 273: return item; 274: } 275: 276: /** 277: * Creates JMenuItem with the associated action and inserts it to the menu 278: * at the specified index. IllegalArgumentException is thrown 279: * if index is less than 0 280: * 281: * @param action Action for the new menu item 282: * @param index index at which to insert newly created menu item. 283: * @return Menu item that was added to the menu 284: */ 285: public JMenuItem insert(Action action, int index) 286: { 287: JMenuItem item = new JMenuItem(action); 288: this.insert(item, index); 289: 290: return item; 291: } 292: 293: /** 294: * This method sets this menuItem's UI to the UIManager's default for the 295: * current look and feel. 296: */ 297: public void updateUI() 298: { 299: super.setUI((MenuItemUI) UIManager.getUI(this)); 300: invalidate(); 301: } 302: 303: /** 304: * This method returns a name to identify which look and feel class will be 305: * the UI delegate for the menu. 306: * 307: * @return The Look and Feel classID. "MenuUI" 308: */ 309: public String getUIClassID() 310: { 311: return "MenuUI"; 312: } 313: 314: /** 315: * Sets model for this menu. 316: * 317: * @param model model to set 318: */ 319: public void setModel(ButtonModel model) 320: { 321: super.setModel(model); 322: } 323: 324: /** 325: * Returns true if the menu is selected and false otherwise 326: * 327: * @return true if the menu is selected and false otherwise 328: */ 329: public boolean isSelected() 330: { 331: return super.isSelected(); 332: } 333: 334: /** 335: * A helper method to handle setSelected calls from both mouse events and 336: * direct calls to setSelected. Direct calls shouldn't expand the popup 337: * menu and should select the JMenu even if it is disabled. Mouse events 338: * only select the JMenu if it is enabled and should expand the popup menu 339: * associated with this JMenu. 340: * @param selected whether or not the JMenu was selected 341: * @param menuEnabled whether or not selecting the menu is "enabled". This 342: * is always true for direct calls, and is set to isEnabled() for mouse 343: * based calls. 344: * @param showMenu whether or not to show the popup menu 345: */ 346: private void setSelectedHelper(boolean selected, boolean menuEnabled, boolean showMenu) 347: { 348: // If menu is selected and enabled, activates the menu and 349: // displays associated popup. 350: if (selected && menuEnabled) 351: { 352: super.setArmed(true); 353: super.setSelected(true); 354: 355: // FIXME: The popup menu should be shown on the screen after certain 356: // number of seconds pass. The 'delay' property of this menu indicates 357: // this amount of seconds. 'delay' property is 0 by default. 358: if (isShowing()) 359: { 360: fireMenuSelected(); 361: 362: int x = 0; 363: int y = 0; 364: if (showMenu) 365: if (menuLocation == null) 366: { 367: // Calculate correct position of the popup. Note that location of the popup 368: // passed to show() should be relative to the popup's invoker 369: if (isTopLevelMenu()) 370: y = this.getHeight(); 371: else 372: x = this.getWidth(); 373: getPopupMenu().show(this, x, y); 374: } 375: else 376: { 377: getPopupMenu().show(this, menuLocation.x, menuLocation.y); 378: } 379: } 380: } 381: 382: else 383: { 384: super.setSelected(false); 385: super.setArmed(false); 386: fireMenuDeselected(); 387: popupMenu.setVisible(false); 388: } 389: } 390: 391: /** 392: * Changes this menu selected state if selected is true and false otherwise 393: * This method fires menuEvents to menu's registered listeners. 394: * 395: * @param selected true if the menu should be selected and false otherwise 396: */ 397: public void setSelected(boolean selected) 398: { 399: setSelectedHelper(selected, true, false); 400: } 401: 402: /** 403: * Checks if PopupMenu associated with this menu is visible 404: * 405: * @return true if the popup associated with this menu is currently visible 406: * on the screen and false otherwise. 407: */ 408: public boolean isPopupMenuVisible() 409: { 410: return popupMenu.isVisible(); 411: } 412: 413: /** 414: * Sets popup menu visibility 415: * 416: * @param popup true if popup should be visible and false otherwise 417: */ 418: public void setPopupMenuVisible(boolean popup) 419: { 420: if (getModel().isEnabled()) 421: popupMenu.setVisible(popup); 422: } 423: 424: /** 425: * Returns origin point of the popup menu 426: * 427: * @return Point containing 428: */ 429: protected Point getPopupMenuOrigin() 430: { 431: // if menu in the menu bar 432: if (isTopLevelMenu()) 433: return new Point(0, this.getHeight()); 434: 435: // if submenu 436: return new Point(this.getWidth(), 0); 437: } 438: 439: /** 440: * Returns delay property. 441: * 442: * @return delay property, indicating number of milliseconds before 443: * popup menu associated with the menu appears or disappears after 444: * menu was selected or deselected respectively 445: */ 446: public int getDelay() 447: { 448: return delay; 449: } 450: 451: /** 452: * Sets delay property for this menu. If given time for the delay 453: * property is negative, then IllegalArgumentException is thrown 454: * 455: * @param delay number of milliseconds before 456: * popup menu associated with the menu appears or disappears after 457: * menu was selected or deselected respectively 458: */ 459: public void setDelay(int delay) 460: { 461: if (delay < 0) 462: throw new IllegalArgumentException("delay less than 0"); 463: this.delay = delay; 464: } 465: 466: /** 467: * Sets location at which popup menu should be displayed 468: * The location given is relative to this menu item 469: * 470: * @param x x-coordinate of the menu location 471: * @param y y-coordinate of the menu location 472: */ 473: public void setMenuLocation(int x, int y) 474: { 475: menuLocation = new Point(x, y); 476: } 477: 478: /** 479: * Creates and returns JMenuItem associated with the given action 480: * 481: * @param action Action to use for creation of JMenuItem 482: * 483: * @return JMenuItem that was creted with given action 484: */ 485: protected JMenuItem createActionComponent(Action action) 486: { 487: return new JMenuItem(action); 488: } 489: 490: /** 491: * Creates ActionChangeListener to listen for PropertyChangeEvents occuring 492: * in the action that is associated with this menu 493: * 494: * @param item menu that contains action to listen to 495: * 496: * @return The PropertyChangeListener 497: */ 498: protected PropertyChangeListener createActionChangeListener(JMenuItem item) 499: { 500: return new ActionChangedListener(item); 501: } 502: 503: /** 504: * Adds separator to the end of the menu items in the menu. 505: */ 506: public void addSeparator() 507: { 508: getPopupMenu().addSeparator(); 509: } 510: 511: /** 512: * Inserts separator in the menu at the specified index. 513: * 514: * @param index Index at which separator should be inserted 515: */ 516: public void insertSeparator(int index) 517: { 518: if (index < 0) 519: throw new IllegalArgumentException("index less than 0"); 520: 521: getPopupMenu().insert(new JPopupMenu.Separator(), index); 522: } 523: 524: /** 525: * Returns menu item located at the specified index in the menu 526: * 527: * @param index Index at which to look for the menu item 528: * 529: * @return menu item located at the specified index in the menu 530: */ 531: public JMenuItem getItem(int index) 532: { 533: if (index < 0) 534: throw new IllegalArgumentException("index less than 0"); 535: 536: Component c = popupMenu.getComponentAtIndex(index); 537: 538: if (c instanceof JMenuItem) 539: return (JMenuItem) c; 540: else 541: return null; 542: } 543: 544: /** 545: * Returns number of items in the menu including separators. 546: * 547: * @return number of items in the menu 548: * 549: * @see #getMenuComponentCount() 550: */ 551: public int getItemCount() 552: { 553: return getMenuComponentCount(); 554: } 555: 556: /** 557: * Checks if this menu is a tear-off menu. 558: * 559: * @return true if this menu is a tear-off menu and false otherwise 560: */ 561: public boolean isTearOff() 562: { 563: // NOT YET IMPLEMENTED 564: return false; 565: } 566: 567: /** 568: * Returns number of menu components in this menu 569: * 570: * @return number of menu components in this menu 571: */ 572: public int getMenuComponentCount() 573: { 574: return popupMenu.getComponentCount(); 575: } 576: 577: /** 578: * Returns menu component located at the givent index 579: * in the menu 580: * 581: * @param index index at which to get the menu component in the menu 582: * 583: * @return Menu Component located in the menu at the specified index 584: */ 585: public Component getMenuComponent(int index) 586: { 587: return (Component) popupMenu.getComponentAtIndex(index); 588: } 589: 590: /** 591: * Return components belonging to this menu 592: * 593: * @return components belonging to this menu 594: */ 595: public Component[] getMenuComponents() 596: { 597: return popupMenu.getComponents(); 598: } 599: 600: /** 601: * Checks if this menu is a top level menu. The menu is top 602: * level menu if it is inside the menu bar. While if the menu 603: * inside some other menu, it is considered to be a pull-right menu. 604: * 605: * @return true if this menu is top level menu, and false otherwise 606: */ 607: public boolean isTopLevelMenu() 608: { 609: return getParent() instanceof JMenuBar; 610: } 611: 612: /** 613: * Checks if given component exists in this menu. The submenus of 614: * this menu are checked as well 615: * 616: * @param component Component to look for 617: * 618: * @return true if the given component exists in this menu, and false otherwise 619: */ 620: public boolean isMenuComponent(Component component) 621: { 622: return false; 623: } 624: 625: /** 626: * Returns popup menu associated with the menu. 627: * 628: * @return popup menu associated with the menu. 629: */ 630: public JPopupMenu getPopupMenu() 631: { 632: return popupMenu; 633: } 634: 635: /** 636: * Adds MenuListener to the menu 637: * 638: * @param listener MenuListener to add 639: */ 640: public void addMenuListener(MenuListener listener) 641: { 642: listenerList.add(MenuListener.class, listener); 643: } 644: 645: /** 646: * Removes MenuListener from the menu 647: * 648: * @param listener MenuListener to remove 649: */ 650: public void removeMenuListener(MenuListener listener) 651: { 652: listenerList.remove(MenuListener.class, listener); 653: } 654: 655: /** 656: * Returns all registered <code>MenuListener</code> objects. 657: * 658: * @return an array of listeners 659: * 660: * @since 1.4 661: */ 662: public MenuListener[] getMenuListeners() 663: { 664: return (MenuListener[]) listenerList.getListeners(MenuListener.class); 665: } 666: 667: /** 668: * This method fires MenuEvents to all menu's MenuListeners. In this case 669: * menuSelected() method of MenuListeners is called to indicated that the menu 670: * was selected. 671: */ 672: protected void fireMenuSelected() 673: { 674: MenuListener[] listeners = getMenuListeners(); 675: 676: for (int index = 0; index < listeners.length; ++index) 677: listeners[index].menuSelected(menuEvent); 678: } 679: 680: /** 681: * This method fires MenuEvents to all menu's MenuListeners. In this case 682: * menuDeselected() method of MenuListeners is called to indicated that the menu 683: * was deselected. 684: */ 685: protected void fireMenuDeselected() 686: { 687: EventListener[] ll = listenerList.getListeners(MenuListener.class); 688: 689: for (int i = 0; i < ll.length; i++) 690: ((MenuListener) ll[i]).menuDeselected(menuEvent); 691: } 692: 693: /** 694: * This method fires MenuEvents to all menu's MenuListeners. In this case 695: * menuSelected() method of MenuListeners is called to indicated that the menu 696: * was cancelled. The menu is cancelled when it's popup menu is close without selection. 697: */ 698: protected void fireMenuCanceled() 699: { 700: EventListener[] ll = listenerList.getListeners(MenuListener.class); 701: 702: for (int i = 0; i < ll.length; i++) 703: ((MenuListener) ll[i]).menuCanceled(menuEvent); 704: } 705: 706: /** 707: * Creates WinListener that listens to the menu;s popup menu. 708: * 709: * @param popup JPopupMenu to listen to 710: * 711: * @return The WinListener 712: */ 713: protected WinListener createWinListener(JPopupMenu popup) 714: { 715: return new WinListener(popup); 716: } 717: 718: /** 719: * Method of the MenuElementInterface. It reacts to the selection 720: * changes in the menu. If this menu was selected, then it 721: * displayes popup menu associated with it and if this menu was 722: * deselected it hides the popup menu. 723: * 724: * @param changed true if the menu was selected and false otherwise 725: */ 726: public void menuSelectionChanged(boolean changed) 727: { 728: // if this menu selection is true, then activate this menu and 729: // display popup associated with this menu 730: setSelectedHelper(changed, isEnabled(), true); 731: } 732: 733: /** 734: * Method of MenuElement interface. Returns sub components of 735: * this menu. 736: * 737: * @return array containing popupMenu that is associated with this menu 738: */ 739: public MenuElement[] getSubElements() 740: { 741: return new MenuElement[] { popupMenu }; 742: } 743: 744: /** 745: * @return Returns reference to itself 746: */ 747: public Component getComponent() 748: { 749: return this; 750: } 751: 752: /** 753: * This method is overriden with empty implementation, s.t the 754: * accelerator couldn't be set for the menu. The mnemonic should 755: * be used for the menu instead. 756: * 757: * @param keystroke accelerator for this menu 758: */ 759: public void setAccelerator(KeyStroke keystroke) 760: { 761: throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead."); 762: } 763: 764: /** 765: * This method process KeyEvent occuring when the menu is visible 766: * 767: * @param event The KeyEvent 768: */ 769: protected void processKeyEvent(KeyEvent event) 770: { 771: } 772: 773: /** 774: * Programatically performs click 775: * 776: * @param time Number of milliseconds for which this menu stays pressed 777: */ 778: public void doClick(int time) 779: { 780: getModel().setArmed(true); 781: getModel().setPressed(true); 782: try 783: { 784: java.lang.Thread.sleep(time); 785: } 786: catch (java.lang.InterruptedException e) 787: { 788: // probably harmless 789: } 790: 791: getModel().setPressed(false); 792: getModel().setArmed(false); 793: popupMenu.show(this, this.getWidth(), 0); 794: } 795: 796: /** 797: * A string that describes this JMenu. Normally only used 798: * for debugging. 799: * 800: * @return A string describing this JMenu 801: */ 802: protected String paramString() 803: { 804: return super.paramString(); 805: } 806: 807: public AccessibleContext getAccessibleContext() 808: { 809: if (accessibleContext == null) 810: accessibleContext = new AccessibleJMenu(); 811: 812: return accessibleContext; 813: } 814: 815: protected class AccessibleJMenu extends AccessibleJMenuItem 816: implements AccessibleSelection 817: { 818: private static final long serialVersionUID = -8131864021059524309L; 819: 820: protected AccessibleJMenu() 821: { 822: } 823: 824: public int getAccessibleChildrenCount() 825: { 826: return 0; 827: } 828: 829: public Accessible getAccessibleChild(int value0) 830: { 831: return null; 832: } 833: 834: public AccessibleSelection getAccessibleSelection() 835: { 836: return null; 837: } 838: 839: public Accessible getAccessibleSelection(int value0) 840: { 841: return null; 842: } 843: 844: public boolean isAccessibleChildSelected(int value0) 845: { 846: return false; 847: } 848: 849: public AccessibleRole getAccessibleRole() 850: { 851: return AccessibleRole.MENU; 852: } 853: 854: public int getAccessibleSelectionCount() 855: { 856: return 0; 857: } 858: 859: public void addAccessibleSelection(int value0) 860: { 861: } 862: 863: public void removeAccessibleSelection(int value0) 864: { 865: } 866: 867: public void clearAccessibleSelection() 868: { 869: } 870: 871: public void selectAllAccessibleSelection() 872: { 873: } 874: } 875: 876: protected class WinListener extends WindowAdapter implements Serializable 877: { 878: JPopupMenu popupMenu; 879: private static final long serialVersionUID = -6415815570638474823L; 880: 881: public WinListener(JPopupMenu popup) 882: { 883: } 884: 885: public void windowClosing(WindowEvent event) 886: { 887: } 888: } 889: 890: /** 891: * This class listens to PropertyChangeEvents occuring in menu's action 892: */ 893: protected class ActionChangedListener implements PropertyChangeListener 894: { 895: /** menu item associated with the action */ 896: private JMenuItem menuItem; 897: 898: /** Creates new ActionChangedListener and adds it to menuItem's action */ 899: public ActionChangedListener(JMenuItem menuItem) 900: { 901: this.menuItem = menuItem; 902: 903: Action a = menuItem.getAction(); 904: if (a != null) 905: a.addPropertyChangeListener(this); 906: } 907: 908: /**This method is invoked when some change occures in menuItem's action*/ 909: public void propertyChange(PropertyChangeEvent evt) 910: { 911: // FIXME: Need to implement 912: } 913: } 914: 915: }
GNU Classpath (0.17) |