| /* JMenu.java -- |
| Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing; |
| |
| import java.awt.Component; |
| import java.awt.Point; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.WindowAdapter; |
| import java.awt.event.WindowEvent; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.IOException; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.util.EventListener; |
| |
| import javax.accessibility.Accessible; |
| import javax.accessibility.AccessibleContext; |
| import javax.accessibility.AccessibleRole; |
| import javax.accessibility.AccessibleSelection; |
| import javax.swing.event.MenuEvent; |
| import javax.swing.event.MenuListener; |
| import javax.swing.plaf.MenuItemUI; |
| |
| /** |
| * This class represents a menu that can be added to a menu bar or |
| * can be a submenu in some other menu. When JMenu is selected it |
| * displays JPopupMenu containing its menu items. |
| * |
| * <p> |
| * JMenu's fires MenuEvents when this menu's selection changes. If this menu |
| * is selected, then fireMenuSelectedEvent() is invoked. In case when menu is |
| * deselected or cancelled, then fireMenuDeselectedEvent() or |
| * fireMenuCancelledEvent() is invoked, respectivelly. |
| * </p> |
| */ |
| public class JMenu extends JMenuItem implements Accessible, MenuElement |
| { |
| private static final long serialVersionUID = 4227225638931828014L; |
| |
| /** A Popup menu associated with this menu, which pops up when menu is selected */ |
| private JPopupMenu popupMenu = new JPopupMenu(); |
| |
| /** Whenever menu is selected or deselected the MenuEvent is fired to |
| menu's registered listeners. */ |
| private MenuEvent menuEvent = new MenuEvent(this); |
| |
| /*Amount of time, in milliseconds, that should pass before popupMenu |
| associated with this menu appears or disappers */ |
| private int delay; |
| |
| /* PopupListener */ |
| protected WinListener popupListener; |
| |
| /** Location at which popup menu associated with this menu will be |
| displayed */ |
| private Point menuLocation; |
| |
| /** |
| * Creates a new JMenu object. |
| */ |
| public JMenu() |
| { |
| super(); |
| } |
| |
| /** |
| * Creates a new <code>JMenu</code> with the specified label. |
| * |
| * @param text label for this menu |
| */ |
| public JMenu(String text) |
| { |
| super(text); |
| popupMenu.setInvoker(this); |
| } |
| |
| /** |
| * Creates a new <code>JMenu</code> object. |
| * |
| * @param action Action that is used to create menu item tha will be |
| * added to the menu. |
| */ |
| public JMenu(Action action) |
| { |
| super(action); |
| createActionChangeListener(this); |
| popupMenu.setInvoker(this); |
| } |
| |
| /** |
| * Creates a new <code>JMenu</code> with specified label and an option |
| * for this menu to be tear-off menu. |
| * |
| * @param text label for this menu |
| * @param tearoff true if this menu should be tear-off and false otherwise |
| */ |
| public JMenu(String text, boolean tearoff) |
| { |
| // FIXME: tearoff not implemented |
| this(text); |
| } |
| |
| private void writeObject(ObjectOutputStream stream) throws IOException |
| { |
| } |
| |
| /** |
| * Adds specified menu item to this menu |
| * |
| * @param item Menu item to add to this menu |
| * |
| * @return Menu item that was added |
| */ |
| public JMenuItem add(JMenuItem item) |
| { |
| return popupMenu.add(item); |
| } |
| |
| /** |
| * Adds specified component to this menu. |
| * |
| * @param component Component to add to this menu |
| * |
| * @return Component that was added |
| */ |
| public Component add(Component component) |
| { |
| return popupMenu.add(component); |
| } |
| |
| /** |
| * Adds specified component to this menu at the given index |
| * |
| * @param component Component to add |
| * @param index Position of this menu item in the menu |
| * |
| * @return Component that was added |
| */ |
| public Component add(Component component, int index) |
| { |
| return popupMenu.add(component, index); |
| } |
| |
| /** |
| * Adds JMenuItem constructed with the specified label to this menu |
| * |
| * @param text label for the menu item that will be added |
| * |
| * @return Menu Item that was added to this menu |
| */ |
| public JMenuItem add(String text) |
| { |
| return popupMenu.add(text); |
| } |
| |
| /** |
| * Adds JMenuItem constructed using properties from specified action. |
| * |
| * @param action action to construct the menu item with |
| * |
| * @return Menu Item that was added to this menu |
| */ |
| public JMenuItem add(Action action) |
| { |
| return popupMenu.add(action); |
| } |
| |
| /** |
| * Removes given menu item from this menu. Nothing happens if |
| * this menu doesn't contain specified menu item. |
| * |
| * @param item Menu Item which needs to be removed |
| */ |
| public void remove(JMenuItem item) |
| { |
| popupMenu.remove(item); |
| } |
| |
| /** |
| * Removes component at the specified index from this menu |
| * |
| * @param index Position of the component that needs to be removed in the menu |
| */ |
| public void remove(int index) |
| { |
| popupMenu.remove(index); |
| } |
| |
| /** |
| * Removes given component from this menu. |
| * |
| * @param component Component to remove |
| */ |
| public void remove(Component component) |
| { |
| int index = popupMenu.getComponentIndex(component); |
| popupMenu.remove(index); |
| } |
| |
| /** |
| * Removes all menu items from the menu |
| */ |
| public void removeAll() |
| { |
| popupMenu.removeAll(); |
| } |
| |
| /** |
| * Creates JMenuItem with the specified text and inserts it in the |
| * at the specified index |
| * |
| * @param text label for the new menu item |
| * @param index index at which to insert newly created menu item. |
| */ |
| public void insert(String text, int index) |
| { |
| this.insert(new JMenuItem(text), index); |
| } |
| |
| /** |
| * Creates JMenuItem with the specified text and inserts it in the |
| * at the specified index. IllegalArgumentException is thrown |
| * if index is less than 0 |
| * |
| * @param item menu item to insert |
| * @param index index at which to insert menu item. |
| * @return Menu item that was added to the menu |
| */ |
| public JMenuItem insert(JMenuItem item, int index) |
| { |
| if (index < 0) |
| throw new IllegalArgumentException("index less than zero"); |
| |
| popupMenu.insert(item, index); |
| return item; |
| } |
| |
| /** |
| * Creates JMenuItem with the associated action and inserts it to the menu |
| * at the specified index. IllegalArgumentException is thrown |
| * if index is less than 0 |
| * |
| * @param action Action for the new menu item |
| * @param index index at which to insert newly created menu item. |
| * @return Menu item that was added to the menu |
| */ |
| public JMenuItem insert(Action action, int index) |
| { |
| JMenuItem item = new JMenuItem(action); |
| this.insert(item, index); |
| |
| return item; |
| } |
| |
| /** |
| * This method sets this menuItem's UI to the UIManager's default for the |
| * current look and feel. |
| */ |
| public void updateUI() |
| { |
| super.setUI((MenuItemUI) UIManager.getUI(this)); |
| invalidate(); |
| } |
| |
| /** |
| * This method returns a name to identify which look and feel class will be |
| * the UI delegate for the menu. |
| * |
| * @return The Look and Feel classID. "MenuUI" |
| */ |
| public String getUIClassID() |
| { |
| return "MenuUI"; |
| } |
| |
| /** |
| * Sets model for this menu. |
| * |
| * @param model model to set |
| */ |
| public void setModel(ButtonModel model) |
| { |
| super.setModel(model); |
| } |
| |
| /** |
| * Returns true if the menu is selected and false otherwise |
| * |
| * @return true if the menu is selected and false otherwise |
| */ |
| public boolean isSelected() |
| { |
| return super.isArmed(); |
| } |
| |
| /** |
| * Changes this menu selected state if selected is true and false otherwise |
| * This method fires menuEvents to menu's registered listeners. |
| * |
| * @param selected true if the menu should be selected and false otherwise |
| */ |
| public void setSelected(boolean selected) |
| { |
| // if this menu selection is true, then activate this menu and |
| // display popup associated with this menu |
| if (selected) |
| { |
| super.setArmed(true); |
| super.setSelected(true); |
| |
| // FIXME: The popup menu should be shown on the screen after certain |
| // number of seconds pass. The 'delay' property of this menu indicates |
| // this amount of seconds. 'delay' property is 0 by default. |
| if (this.isShowing()) |
| { |
| fireMenuSelected(); |
| |
| int x = 0; |
| int y = 0; |
| |
| if (menuLocation == null) |
| { |
| // Calculate correct position of the popup. Note that location of the popup |
| // passed to show() should be relative to the popup's invoker |
| if (isTopLevelMenu()) |
| y = this.getHeight(); |
| else |
| x = this.getWidth(); |
| |
| getPopupMenu().show(this, x, y); |
| } |
| else |
| getPopupMenu().show(this, menuLocation.x, menuLocation.y); |
| } |
| } |
| |
| else |
| { |
| super.setSelected(false); |
| super.setArmed(false); |
| fireMenuDeselected(); |
| popupMenu.setVisible(false); |
| } |
| } |
| |
| /** |
| * Checks if PopupMenu associated with this menu is visible |
| * |
| * @return true if the popup associated with this menu is currently visible |
| * on the screen and false otherwise. |
| */ |
| public boolean isPopupMenuVisible() |
| { |
| return popupMenu.isVisible(); |
| } |
| |
| /** |
| * Sets popup menu visibility |
| * |
| * @param popup true if popup should be visible and false otherwise |
| */ |
| public void setPopupMenuVisible(boolean popup) |
| { |
| if (getModel().isEnabled()) |
| popupMenu.setVisible(popup); |
| } |
| |
| /** |
| * Returns origin point of the popup menu |
| * |
| * @return Point containing |
| */ |
| protected Point getPopupMenuOrigin() |
| { |
| // if menu in the menu bar |
| if (isTopLevelMenu()) |
| return new Point(0, this.getHeight()); |
| |
| // if submenu |
| return new Point(this.getWidth(), 0); |
| } |
| |
| /** |
| * Returns delay property. |
| * |
| * @return delay property, indicating number of milliseconds before |
| * popup menu associated with the menu appears or disappears after |
| * menu was selected or deselected respectively |
| */ |
| public int getDelay() |
| { |
| return delay; |
| } |
| |
| /** |
| * Sets delay property for this menu. If given time for the delay |
| * property is negative, then IllegalArgumentException is thrown |
| * |
| * @param delay number of milliseconds before |
| * popup menu associated with the menu appears or disappears after |
| * menu was selected or deselected respectively |
| */ |
| public void setDelay(int delay) |
| { |
| if (delay < 0) |
| throw new IllegalArgumentException("delay less than 0"); |
| this.delay = delay; |
| } |
| |
| /** |
| * Sets location at which popup menu should be displayed |
| * The location given is relative to this menu item |
| * |
| * @param x x-coordinate of the menu location |
| * @param y y-coordinate of the menu location |
| */ |
| public void setMenuLocation(int x, int y) |
| { |
| menuLocation = new Point(x, y); |
| } |
| |
| /** |
| * Creates and returns JMenuItem associated with the given action |
| * |
| * @param action Action to use for creation of JMenuItem |
| * |
| * @return JMenuItem that was creted with given action |
| */ |
| protected JMenuItem createActionComponent(Action action) |
| { |
| return new JMenuItem(action); |
| } |
| |
| /** |
| * Creates ActionChangeListener to listen for PropertyChangeEvents occuring |
| * in the action that is associated with this menu |
| * |
| * @param item menu that contains action to listen to |
| * |
| * @return The PropertyChangeListener |
| */ |
| protected PropertyChangeListener createActionChangeListener(JMenuItem item) |
| { |
| return new ActionChangedListener(item); |
| } |
| |
| /** |
| * Adds separator to the end of the menu items in the menu. |
| */ |
| public void addSeparator() |
| { |
| getPopupMenu().addSeparator(); |
| } |
| |
| /** |
| * Inserts separator in the menu at the specified index. |
| * |
| * @param index Index at which separator should be inserted |
| */ |
| public void insertSeparator(int index) |
| { |
| if (index < 0) |
| throw new IllegalArgumentException("index less than 0"); |
| |
| getPopupMenu().insert(new JPopupMenu.Separator(), index); |
| } |
| |
| /** |
| * Returns menu item located at the specified index in the menu |
| * |
| * @param index Index at which to look for the menu item |
| * |
| * @return menu item located at the specified index in the menu |
| */ |
| public JMenuItem getItem(int index) |
| { |
| if (index < 0) |
| throw new IllegalArgumentException("index less than 0"); |
| |
| Component c = popupMenu.getComponentAtIndex(index); |
| |
| if (c instanceof JMenuItem) |
| return (JMenuItem) c; |
| else |
| return null; |
| } |
| |
| /** |
| * Returns number of items in the menu including separators. |
| * |
| * @return number of items in the menu |
| * |
| * @see #getMenuComponentCount() |
| */ |
| public int getItemCount() |
| { |
| return getMenuComponentCount(); |
| } |
| |
| /** |
| * Checks if this menu is a tear-off menu. |
| * |
| * @return true if this menu is a tear-off menu and false otherwise |
| */ |
| public boolean isTearOff() |
| { |
| // NOT YET IMPLEMENTED |
| return false; |
| } |
| |
| /** |
| * Returns number of menu components in this menu |
| * |
| * @return number of menu components in this menu |
| */ |
| public int getMenuComponentCount() |
| { |
| return popupMenu.getComponentCount(); |
| } |
| |
| /** |
| * Returns menu component located at the givent index |
| * in the menu |
| * |
| * @param index index at which to get the menu component in the menu |
| * |
| * @return Menu Component located in the menu at the specified index |
| */ |
| public Component getMenuComponent(int index) |
| { |
| return (Component) popupMenu.getComponentAtIndex(index); |
| } |
| |
| /** |
| * Return components belonging to this menu |
| * |
| * @return components belonging to this menu |
| */ |
| public Component[] getMenuComponents() |
| { |
| return popupMenu.getComponents(); |
| } |
| |
| /** |
| * Checks if this menu is a top level menu. The menu is top |
| * level menu if it is inside the menu bar. While if the menu |
| * inside some other menu, it is considered to be a pull-right menu. |
| * |
| * @return true if this menu is top level menu, and false otherwise |
| */ |
| public boolean isTopLevelMenu() |
| { |
| return getParent() instanceof JMenuBar; |
| } |
| |
| /** |
| * Checks if given component exists in this menu. The submenus of |
| * this menu are checked as well |
| * |
| * @param component Component to look for |
| * |
| * @return true if the given component exists in this menu, and false otherwise |
| */ |
| public boolean isMenuComponent(Component component) |
| { |
| return false; |
| } |
| |
| /** |
| * Returns popup menu associated with the menu. |
| * |
| * @return popup menu associated with the menu. |
| */ |
| public JPopupMenu getPopupMenu() |
| { |
| return popupMenu; |
| } |
| |
| /** |
| * Adds MenuListener to the menu |
| * |
| * @param listener MenuListener to add |
| */ |
| public void addMenuListener(MenuListener listener) |
| { |
| listenerList.add(MenuListener.class, listener); |
| } |
| |
| /** |
| * Removes MenuListener from the menu |
| * |
| * @param listener MenuListener to remove |
| */ |
| public void removeMenuListener(MenuListener listener) |
| { |
| listenerList.remove(MenuListener.class, listener); |
| } |
| |
| /** |
| * Returns all registered <code>MenuListener</code> objects. |
| * |
| * @return an array of listeners |
| * |
| * @since 1.4 |
| */ |
| public MenuListener[] getMenuListeners() |
| { |
| return (MenuListener[]) listenerList.getListeners(MenuListener.class); |
| } |
| |
| /** |
| * This method fires MenuEvents to all menu's MenuListeners. In this case |
| * menuSelected() method of MenuListeners is called to indicated that the menu |
| * was selected. |
| */ |
| protected void fireMenuSelected() |
| { |
| MenuListener[] listeners = getMenuListeners(); |
| |
| for (int index = 0; index < listeners.length; ++index) |
| listeners[index].menuSelected(menuEvent); |
| } |
| |
| /** |
| * This method fires MenuEvents to all menu's MenuListeners. In this case |
| * menuDeselected() method of MenuListeners is called to indicated that the menu |
| * was deselected. |
| */ |
| protected void fireMenuDeselected() |
| { |
| EventListener[] ll = listenerList.getListeners(MenuListener.class); |
| |
| for (int i = 0; i < ll.length; i++) |
| ((MenuListener) ll[i]).menuDeselected(menuEvent); |
| } |
| |
| /** |
| * This method fires MenuEvents to all menu's MenuListeners. In this case |
| * menuSelected() method of MenuListeners is called to indicated that the menu |
| * was cancelled. The menu is cancelled when it's popup menu is close without selection. |
| */ |
| protected void fireMenuCanceled() |
| { |
| EventListener[] ll = listenerList.getListeners(MenuListener.class); |
| |
| for (int i = 0; i < ll.length; i++) |
| ((MenuListener) ll[i]).menuCanceled(menuEvent); |
| } |
| |
| /** |
| * Creates WinListener that listens to the menu;s popup menu. |
| * |
| * @param popup JPopupMenu to listen to |
| * |
| * @return The WinListener |
| */ |
| protected WinListener createWinListener(JPopupMenu popup) |
| { |
| return new WinListener(popup); |
| } |
| |
| /** |
| * Method of the MenuElementInterface. It reacts to the selection |
| * changes in the menu. If this menu was selected, then it |
| * displayes popup menu associated with it and if this menu was |
| * deselected it hides the popup menu. |
| * |
| * @param changed true if the menu was selected and false otherwise |
| */ |
| public void menuSelectionChanged(boolean changed) |
| { |
| // if this menu selection is true, then activate this menu and |
| // display popup associated with this menu |
| setSelected(changed); |
| } |
| |
| /** |
| * Method of MenuElement interface. Returns sub components of |
| * this menu. |
| * |
| * @return array containing popupMenu that is associated with this menu |
| */ |
| public MenuElement[] getSubElements() |
| { |
| return new MenuElement[] { popupMenu }; |
| } |
| |
| /** |
| * @return Returns reference to itself |
| */ |
| public Component getComponent() |
| { |
| return this; |
| } |
| |
| /** |
| * This method is overriden with empty implementation, s.t the |
| * accelerator couldn't be set for the menu. The mnemonic should |
| * be used for the menu instead. |
| * |
| * @param keystroke accelerator for this menu |
| */ |
| public void setAccelerator(KeyStroke keystroke) |
| { |
| throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead."); |
| } |
| |
| /** |
| * This method process KeyEvent occuring when the menu is visible |
| * |
| * @param event The KeyEvent |
| */ |
| protected void processKeyEvent(KeyEvent event) |
| { |
| } |
| |
| /** |
| * Programatically performs click |
| * |
| * @param time Number of milliseconds for which this menu stays pressed |
| */ |
| public void doClick(int time) |
| { |
| getModel().setArmed(true); |
| getModel().setPressed(true); |
| try |
| { |
| java.lang.Thread.sleep(time); |
| } |
| catch (java.lang.InterruptedException e) |
| { |
| // probably harmless |
| } |
| |
| getModel().setPressed(false); |
| getModel().setArmed(false); |
| popupMenu.show(this, this.getWidth(), 0); |
| } |
| |
| /** |
| * A string that describes this JMenu. Normally only used |
| * for debugging. |
| * |
| * @return A string describing this JMenu |
| */ |
| protected String paramString() |
| { |
| return super.paramString(); |
| } |
| |
| public AccessibleContext getAccessibleContext() |
| { |
| if (accessibleContext == null) |
| accessibleContext = new AccessibleJMenu(); |
| |
| return accessibleContext; |
| } |
| |
| protected class AccessibleJMenu extends AccessibleJMenuItem |
| implements AccessibleSelection |
| { |
| protected AccessibleJMenu() |
| { |
| } |
| |
| public int getAccessibleChildrenCount() |
| { |
| return 0; |
| } |
| |
| public Accessible getAccessibleChild(int value0) |
| { |
| return null; |
| } |
| |
| public AccessibleSelection getAccessibleSelection() |
| { |
| return null; |
| } |
| |
| public Accessible getAccessibleSelection(int value0) |
| { |
| return null; |
| } |
| |
| public boolean isAccessibleChildSelected(int value0) |
| { |
| return false; |
| } |
| |
| public AccessibleRole getAccessibleRole() |
| { |
| return AccessibleRole.MENU; |
| } |
| |
| public int getAccessibleSelectionCount() |
| { |
| return 0; |
| } |
| |
| public void addAccessibleSelection(int value0) |
| { |
| } |
| |
| public void removeAccessibleSelection(int value0) |
| { |
| } |
| |
| public void clearAccessibleSelection() |
| { |
| } |
| |
| public void selectAllAccessibleSelection() |
| { |
| } |
| } |
| |
| protected class WinListener extends WindowAdapter implements Serializable |
| { |
| JPopupMenu popupMenu; |
| private static final long serialVersionUID = -6415815570638474823L; |
| |
| public WinListener(JPopupMenu popup) |
| { |
| } |
| |
| public void windowClosing(WindowEvent event) |
| { |
| } |
| } |
| |
| /** |
| * This class listens to PropertyChangeEvents occuring in menu's action |
| */ |
| protected class ActionChangedListener implements PropertyChangeListener |
| { |
| /** menu item associated with the action */ |
| private JMenuItem menuItem; |
| |
| /** Creates new ActionChangedListener and adds it to menuItem's action */ |
| public ActionChangedListener(JMenuItem menuItem) |
| { |
| this.menuItem = menuItem; |
| |
| Action a = menuItem.getAction(); |
| if (a != null) |
| a.addPropertyChangeListener(this); |
| } |
| |
| /**This method is invoked when some change occures in menuItem's action*/ |
| public void propertyChange(PropertyChangeEvent evt) |
| { |
| // FIXME: Need to implement |
| } |
| } |
| } |