home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / awt / swing / JMenu.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  32.8 KB  |  1,024 lines

  1. /*
  2.  * @(#)JMenu.java    1.86 98/02/04
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package java.awt.swing;
  22.  
  23. import java.awt.Component;
  24. import java.awt.Container;
  25. import java.awt.Dimension;
  26. import java.awt.Frame;
  27. import java.awt.Graphics;
  28. import java.awt.LayoutManager;
  29. import java.awt.Point;
  30. import java.awt.Polygon;
  31. import java.awt.Rectangle;
  32. import java.awt.event.*;
  33. import java.beans.*;
  34.  
  35. import java.util.*;
  36. import java.io.Serializable;
  37.  
  38. import java.awt.swing.event.*;
  39. import java.awt.swing.plaf.*;
  40. import java.awt.accessibility.*;
  41.  
  42.  
  43. /**
  44.  * An implementation of a menu -- a popup window containing <code>JMenuItem</code>s that
  45.  * is displayed when the user selects an item on the <code>JMenuBar</code>. In addition
  46.  * to JMenuItems, a JMenu can also contain <code>JSeparator</code>s. 
  47.  * <p>
  48.  * In essence, a menu is a button with an associated JPopupMenu.
  49.  * When the "button" is pressed, the JPopupMenu appears. If the
  50.  * "button" is on the JMenuBar, the menu is a top-level window.
  51.  * If the "button" is another menu item, then the JPopupMenu is
  52.  * "pull-right" menu.
  53.  * <p>
  54.  * Warning: serialized objects of this class will not be compatible with
  55.  * future swing releases.  The current serialization support is appropriate 
  56.  * for short term storage or RMI between Swing1.0 applications.  It will
  57.  * not be possible to load serialized Swing1.0 objects with future releases
  58.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  59.  * baseline for the serialized form of Swing objects.
  60.  *
  61.  * @version 1.86 02/04/98
  62.  * @author Georges Saab
  63.  * @author David Karlton
  64.  * @author Arnaud Weber
  65.  * @see JMenuItem
  66.  * @see JSeparator
  67.  * @see JMenuBar
  68.  * @see JPopupMenu
  69.  */
  70. public class JMenu extends JMenuItem implements Accessible,MenuElement
  71. {
  72.     /*
  73.      * The popup menu portion of the menu.
  74.      */
  75.     private JPopupMenu popupMenu;
  76.  
  77.     /*
  78.      * The button's model listeners.
  79.      */
  80.     private ChangeListener menuChangeListener = null;
  81.  
  82.     /*
  83.      * Only one MenuEvent is needed per menu instance since the
  84.      * event's only state is the source property.  The source of events
  85.      * generated is always "this".
  86.      */
  87.     private MenuEvent menuEvent = null;
  88.  
  89.     /**
  90.      * Creates a new JMenu with no text.
  91.      */
  92.     public JMenu() {
  93.         this("");
  94.     }
  95.  
  96.     /**
  97.      * Creates a new JMenu with the supplied string as its text
  98.      *
  99.      * @param s  The text for the menu label
  100.      */
  101.     public JMenu(String s) {
  102.         init(s, null);
  103.         updateUI();
  104.     }
  105.  
  106.     /**
  107.      * Creates a new JMenu with the supplied string as its text
  108.      * and specified as a tear-off menu or not.
  109.      *
  110.      * @param s The text for the menu label
  111.      * @param b can the menu be torn off (not yet implemented)
  112.      */
  113.     public JMenu(String s, boolean b) {
  114.         this(s);
  115.     }
  116.  
  117.     /**
  118.      * Sets the L&F object that renders this component.
  119.      *
  120.      * @param ui the new MenuUI
  121.      * @see getUI
  122.      * @beaninfo
  123.      * description: The menu item's UI delegate
  124.      *       bound: true
  125.      *      expert: true
  126.      *      hidden: true
  127.      */
  128.     public void setUI(MenuUI ui) {
  129.         super.setUI(ui);
  130.     }
  131.     
  132.     /**
  133.      * Notification from the UIFactory that the L&F has changed. 
  134.      * Called to replace the UI with the latest version from the 
  135.      * UIFactory.
  136.      *
  137.      * @see JComponent#updateUI
  138.      */
  139.     public void updateUI() {
  140.         setUI((MenuUI)UIManager.getUI(this));
  141.  
  142.         if ( popupMenu != null )
  143.           {
  144.             popupMenu.setUI((PopupMenuUI)UIManager.getUI(popupMenu));
  145.           }
  146.  
  147.     }
  148.  
  149.  
  150.     /**
  151.      * Returns the name of the L&F class that renders this component.
  152.      *
  153.      * @return "MenuUI"
  154.      * @see JComponent#getUIClassID
  155.      * @see UIDefaults#getUI
  156.      */
  157.     public String getUIClassID() {
  158.         return "MenuUI";
  159.     }
  160.  
  161.     //    public void repaint(long tm, int x, int y, int width, int height) {
  162.     //        Thread.currentThread().dumpStack();
  163.     //        super.repaint(tm,x,y,width,height);
  164.     //    }
  165.  
  166.     /**
  167.      * Set the data model for the "menu button" -- the label
  168.      * that the user clicks to open or close the menu.
  169.      *
  170.      * @param m the ButtonModel
  171.      * @see getModel
  172.      * @beaninfo
  173.      * description: The menu's model
  174.      *       bound: true
  175.      *      expert: true
  176.      *      hidden: true
  177.      */
  178.     public void setModel(ButtonModel newModel) {
  179.         ButtonModel oldModel = getModel();
  180.  
  181.         super.setModel(newModel);
  182.  
  183.         if (oldModel != null) {
  184.             oldModel.removeChangeListener(menuChangeListener);
  185.             menuChangeListener = null;
  186.         }
  187.         
  188.         model = newModel;
  189.         
  190.         if (newModel != null) {
  191.             menuChangeListener = createMenuChangeListener();
  192.             newModel.addChangeListener(menuChangeListener);
  193.         }
  194.     }
  195.  
  196.     /**
  197.      * Returns true if the menu is currently selected (popped up).
  198.      *
  199.      * @return true if the menu is open, else false
  200.      */
  201.     public boolean isSelected() {
  202.         return getModel().isSelected();
  203.     }
  204.  
  205.     /**
  206.      * Sets the selection status of the menu.
  207.      *
  208.      * @param b  a boolean value -- true to select the menu and 
  209.      *           open it, false to unselect the menu and close it
  210.      * @beaninfo
  211.      *      description: When the menu is selected, its popup child is shown.
  212.      *           expert: true
  213.      *           hidden: true
  214.      */
  215.     public void setSelected(boolean b) {
  216.         ButtonModel model = getModel();
  217.         boolean oldValue = model.isSelected();
  218.  
  219.         if ((accessibleContext != null) && (oldValue != b)) {
  220.             if (b) {
  221.                  accessibleContext.firePropertyChange(
  222.                  AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  223.              null, AccessibleState.SELECTED);
  224.             } else {
  225.                  accessibleContext.firePropertyChange(
  226.                          AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  227.                  AccessibleState.SELECTED, null);
  228.             }
  229.         }
  230.         if (b != model.isSelected()) {
  231.             getModel().setSelected(b);
  232.         }
  233.     }
  234.  
  235.     /**
  236.      * Returns true if the menu's popup window is visible.
  237.      *
  238.      * @return true if the menu is visible, else false
  239.      */
  240.     public boolean isPopupMenuVisible() {
  241.         ensurePopupMenuCreated();
  242.         return popupMenu.isVisible();
  243.     }
  244.  
  245.     /**
  246.      * Set the visibility of the Menu's popup portion.  The popup
  247.      * may only be made visible if the menu is itself showing on
  248.      * the screen.
  249.      *
  250.      * @param b  a boolean value -- true to make the menu visible,
  251.      *           false to hide it
  252.      * @beaninfo
  253.      *      description: The popup menu's visibility
  254.      *           expert: true
  255.      *           hidden: true
  256.      */
  257.     public void setPopupMenuVisible(boolean b) {
  258.         boolean isVisible = isPopupMenuVisible();
  259.         if (b != isVisible) {
  260.             ensurePopupMenuCreated();
  261.             // Set location of popupMenu (pulldown or pullright)
  262.             //  Perhaps this should be dictated by L&F
  263.             int x = 0;
  264.             int y = 0;
  265.             if ((b==true) && isShowing()) {
  266.                 Container parent = getParent();
  267.                 Dimension s = getSize();
  268.                 if (parent instanceof JPopupMenu) {
  269.                     // System.out.println("Pullright: " + getText());
  270.                     x = s.width;
  271.                     y = 0;
  272.                 } else {
  273.                     // System.out.println("Pulldown: " + getText());
  274.                     x = 0;
  275.                     y = s.height;
  276.                 }
  277.                 
  278.                 popupMenu.show(this, x, y);
  279.             } else {
  280.                 popupMenu.setVisible(false);
  281.             }
  282.         }
  283.  
  284.     }
  285.  
  286.     private int delay = 0;
  287.  
  288.     /**
  289.      * Returns the suggested delay before the menu's PopupMenu is popped up or down.
  290.      *
  291.      * @return an int -- the number of milliseconds to delay
  292.      */
  293.     public int getDelay() {
  294.         return delay;
  295.     }
  296.     
  297.     /**
  298.      * Sets the suggested delay before the menu's PopupMenu is popped up or down.
  299.      *
  300.      * @param       d the number of milliseconds to delay
  301.      * @exception   IllegalArgumentException if the value of 
  302.      *                       <code>d</code> is less than 0.
  303.      * @beaninfo
  304.      *      description: The delay between menu selection and making the popup menu visible
  305.      *           expert: true
  306.      */
  307.     public void setDelay(int d) {
  308.         if (d < 0)
  309.             throw new IllegalArgumentException("Delay must be a positive integer");
  310.         
  311.         delay = d;
  312.     }
  313.  
  314.     /**
  315.      * The window-closing listener for the popup.
  316.      *
  317.      * @see WinListener
  318.      */
  319.     protected WinListener popupListener;
  320.  
  321.     private void ensurePopupMenuCreated() {
  322.         if (popupMenu == null) {            
  323.             final JMenu thisMenu = this;
  324.             this.popupMenu = new JPopupMenu();
  325.         popupMenu.setInvoker(this);
  326.             popupListener = createWinListener(popupMenu);
  327.             popupMenu.addPopupMenuListener(new PopupMenuListener() {
  328.                 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
  329.                 }
  330.                 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
  331.                 }
  332.                 public void popupMenuCanceled(PopupMenuEvent e) {
  333.                     ((MenuUI)getUI()).menuCanceled(thisMenu);
  334.                 }
  335.             });
  336.         }
  337.     }
  338.  
  339.     /**
  340.      * Set the location of the popup component
  341.      *
  342.      * @param x the x coordinate of the popup's new position
  343.      * @param y the y coordinate of the popup's new position
  344.      */
  345.     public void setMenuLocation(int x, int y) {
  346.         popupMenu.setLocation(x, y);
  347.     }
  348.  
  349.     /**
  350.      * Appends a menuitem to the end of this menu. 
  351.      * Returns the menuitem added.
  352.      *
  353.      * @param menuItem the JMenuitem to be added
  354.      * @return the JMenuItem added
  355.      */
  356.     public JMenuItem add(JMenuItem menuItem) {
  357.         AccessibleContext ac = menuItem.getAccessibleContext();
  358.         ac.setAccessibleParent(this);
  359.         ensurePopupMenuCreated();
  360.         return popupMenu.add(menuItem);
  361.     }
  362.  
  363.     /**
  364.      * Appends a component to the end of this menu.
  365.      * Returns the component added.
  366.      *
  367.      * @param c the Component to add
  368.      * @return the Component added
  369.      */
  370.     public Component add(Component c) {
  371.         AccessibleContext ac = ((JComponent) c).getAccessibleContext();
  372.         ac.setAccessibleParent(this);
  373.         ensurePopupMenuCreated();
  374.         popupMenu.add(c);
  375.         return c;
  376.     }
  377.  
  378.     /**
  379.      * Creates a new menuitem with the specified text and appends
  380.      * it to the end of this menu.
  381.      *  
  382.      * @param s the string for the menuitem to be added
  383.      */
  384.     public void add(String s) {
  385.         add(new JMenuItem(s));
  386.     }
  387.  
  388.     /**
  389.      * Creates a new menuitem attached to the specified 
  390.      * Action object and appends it to the end of this menu.
  391.      *
  392.      * @param a the Action for the menuitem to be added
  393.      * @see Action
  394.      */
  395.     public JMenuItem add(Action a) {
  396.         JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME),
  397.                      (Icon)a.getValue(Action.SMALL_ICON));
  398.     mi.setHorizontalTextPosition(JButton.RIGHT);
  399.     mi.setVerticalTextPosition(JButton.CENTER);
  400.     mi.setEnabled(a.isEnabled());    
  401.         mi.addActionListener(a);
  402.         add(mi);
  403.     PropertyChangeListener actionPropertyChangeListener = 
  404.         createActionChangeListener(mi);
  405.         a.addPropertyChangeListener(actionPropertyChangeListener);
  406.         return mi;
  407.     }
  408.  
  409.     protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
  410.     return new ActionChangedListener(b);
  411.     }
  412.  
  413.     private class ActionChangedListener implements PropertyChangeListener {
  414.         JMenuItem menuItem;
  415.         
  416.         ActionChangedListener(JMenuItem mi) {
  417.             super();
  418.             this.menuItem = mi;
  419.         }
  420.         public void propertyChange(PropertyChangeEvent e) {
  421.             String propertyName = e.getPropertyName();
  422.             if (e.getPropertyName().equals(Action.NAME)) {
  423.                 String text = (String) e.getNewValue();
  424.                 menuItem.setText(text);
  425.             } else if (propertyName.equals("enabled")) {
  426.                 Boolean enabledState = (Boolean) e.getNewValue();
  427.                 menuItem.setEnabled(enabledState.booleanValue());
  428.             } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
  429.                 Icon icon = (Icon) e.getNewValue();
  430.                 menuItem.setIcon(icon);
  431.                 menuItem.invalidate();
  432.                 menuItem.repaint();
  433.             } 
  434.         }
  435.     }
  436.  
  437.     /**
  438.      * Append a new separator to the end of the menu.
  439.      */
  440.     public void addSeparator() {
  441.         ensurePopupMenuCreated();
  442.         popupMenu.addSeparator();
  443.     }
  444.  
  445.     /**
  446.      * Insert a new menuitem with the specified text at a 
  447.      * given position.
  448.      *
  449.      * @param s the text for the menuitem to add
  450.      * @param pos an int giving the position at which to add the 
  451.      *               new menuitem
  452.      */
  453.     public void insert(String s, int pos) {
  454.         if (pos < 0) {
  455.             throw new IllegalArgumentException("index less than zero.");
  456.         }
  457.  
  458.         ensurePopupMenuCreated();
  459.         popupMenu.insert(new JMenuItem(s), pos);
  460.     }
  461.  
  462.     /**
  463.      * Insert the specified JMenuitem at a given position.
  464.      *
  465.      * @param mi the JMenuitem to add
  466.      * @param pos an int giving the position at which to add the 
  467.      *               new JMenuitem
  468.      */
  469.     public JMenuItem insert(JMenuItem mi, int pos) {
  470.         if (pos < 0) {
  471.             throw new IllegalArgumentException("index less than zero.");
  472.         }
  473.         AccessibleContext ac = mi.getAccessibleContext();
  474.         ac.setAccessibleParent(this);
  475.         ensurePopupMenuCreated();
  476.         popupMenu.insert(mi, pos);
  477.         return mi;
  478.     }
  479.  
  480.     /**
  481.      * Insert a new menuitem attached to the specified Action 
  482.      * object at a given position.
  483.      *
  484.      * @param a the Action object for the menuitem to add
  485.      * @param pos an int giving the position at which to add the 
  486.      *               new menuitem
  487.      */
  488.     public JMenuItem insert(Action a, int pos) {
  489.         if (pos < 0) {
  490.             throw new IllegalArgumentException("index less than zero.");
  491.         }
  492.  
  493.         ensurePopupMenuCreated();
  494.         JMenuItem mi = new JMenuItem((String)a.getValue(Action.NAME));
  495.         mi.addActionListener(a);
  496.         popupMenu.insert(mi, pos);
  497.         a.addPropertyChangeListener(new ActionChangedListener(mi));
  498.         return mi;
  499.     }
  500.  
  501.     /**
  502.      * Inserts a separator at the specified position.
  503.      *
  504.      * @param       index an int giving the position at which to 
  505.      *                    insert the menu separator
  506.      * @exception   IllegalArgumentException if the value of 
  507.      *                       <code>index</code> is less than 0.
  508.      */
  509.     public void insertSeparator(int index) {
  510.         if (index < 0) {
  511.             throw new IllegalArgumentException("index less than zero.");
  512.         }
  513.  
  514.         ensurePopupMenuCreated();
  515.         popupMenu.insert(new JSeparator(), index);
  516.     }
  517.  
  518.     /** 
  519.      * Returns the JMenuItem at the specified position.
  520.      * If the specified position contains a separator, this JMenu
  521.      * is returned.  
  522.      *
  523.      * @param pos    an int giving the position
  524.      * @exception   IllegalArgumentException if the value of 
  525.      *                       <code>index</code> is less than 0.
  526.      */
  527.     public JMenuItem getItem(int pos) {
  528.         if (pos < 0) {
  529.             throw new IllegalArgumentException("index less than zero.");
  530.         }
  531.  
  532.         Component c = getMenuComponent(pos);
  533.         if (c instanceof JMenuItem) {
  534.             JMenuItem mi = (JMenuItem) c;
  535.             return mi;
  536.         }
  537.         // PENDING(ges): Should probably do something better here
  538.         return this;
  539.     }
  540.  
  541.     /**
  542.      * Returns the number of items on the menu, including separators.
  543.      * This method is included for AWT compatibility.
  544.      *
  545.      * @return an int equal to the number of items on the menu
  546.      * @see getMenuComponentCount
  547.      */
  548.     public int getItemCount() {
  549.     return getMenuComponentCount();
  550.     }
  551.  
  552.     /**
  553.      * Returns true if the menu can be torn off.
  554.      *
  555.      * @return true if the menu can be torn off, else false
  556.      */
  557.     public boolean isTearOff() {
  558.         throw new Error("boolean isTearOff() {} not yet implemented");
  559.     }
  560.  
  561.     /**
  562.      * Removes the specified menu item from this menu.
  563.      *
  564.      * @param       item the JMenuItem to be removed from the menu
  565.      */
  566.     public void remove(JMenuItem item) {
  567.         popupMenu.remove(item);
  568.     }
  569.  
  570.     /**
  571.      * Removes the menu item at the specified index from this menu.
  572.      *
  573.      * @param       index the position of the item to be removed. 
  574.      * @exception   IllegalArgumentException if the value of 
  575.      *                       <code>index</code> is less than 0.
  576.      */
  577.     public void remove(int pos) {
  578.         if (pos < 0) {
  579.             throw new IllegalArgumentException("index less than zero.");
  580.         }
  581.  
  582.         popupMenu.remove(pos);
  583.     }
  584.  
  585.     /**
  586.      * Remove all menu items from this menu.
  587.      */
  588.     public void removeAll() {
  589.         int nitems = getMenuComponentCount();
  590.         for (int i = 0 ; i < nitems ; i++) {
  591.             remove(0);
  592.         }
  593.     }
  594.  
  595.     /**
  596.      * Returns the number of components on the menu.
  597.      *
  598.      * @return an int -- the number of components on the menu
  599.      */
  600.     public int getMenuComponentCount() {
  601.         int componentCount = 0;
  602.         if (popupMenu != null)
  603.             componentCount = popupMenu.getComponentCount();
  604.         return componentCount;
  605.     }
  606.  
  607.     /**
  608.      * Returns the component at position n
  609.      *
  610.      * @param n the position of the component to be returned
  611.      */
  612.     public Component getMenuComponent(int n) {
  613.         if (popupMenu != null)
  614.             return popupMenu.getComponent(n);
  615.         
  616.         return null;
  617.     }
  618.  
  619.     /**
  620.      * Returns an array of the menu's subcomponents
  621.      *
  622.      * @return an array of Components
  623.      */
  624.     public Component[] getMenuComponents() {
  625.         if (popupMenu != null)
  626.             return popupMenu.getComponents();
  627.         
  628.         return null;
  629.     }
  630.  
  631.     /**
  632.      * Gets the parameter string representing the state of this menu. 
  633.      * This string is useful for debugging.
  634.      *
  635.      * @return a String containing the menu parameters
  636.      */
  637.     public String paramString() {
  638.         String str = ", JMenu";
  639.         return super.paramString() + str;
  640.     }
  641.  
  642.     /**
  643.      * Returns true if the menu is a 'top-level menu', that is, if it is
  644.      * the direct child of a menubar.
  645.      *
  646.      * @return true if the menu is activated from the menu bar,
  647.      *         false if the menu is activated from a menu item
  648.      *         on another menu
  649.      */
  650.     public boolean isTopLevelMenu() {
  651.         if (getParent() instanceof JMenuBar)
  652.             return true;
  653.         
  654.         return false;
  655.     }
  656.  
  657.     /**
  658.      * Returns true if the specified component exists in the 
  659.      * submenu hierarchy.
  660.      *
  661.      * @param c the Component to be tested
  662.      * @return true if the component exists
  663.      */
  664.     public boolean isMenuComponent(Component c) {
  665.         // Are we in the MenuItem part of the menu
  666.         if (c == this)
  667.             return true;
  668.         // Are we in the PopupMenu?
  669.         if (c instanceof JPopupMenu) {
  670.             JPopupMenu comp = (JPopupMenu) c;
  671.             if (comp == this.getPopupMenu())
  672.                 return true;
  673.         }
  674.         // Are we in a Component on the PopupMenu
  675.         int ncomponents = this.getMenuComponentCount();
  676.         Component[] component = this.getMenuComponents();
  677.         for (int i = 0 ; i < ncomponents ; i++) {
  678.             Component comp = component[i];
  679.             // Are we in the current component?
  680.             if (comp == c)
  681.                 return true;
  682.             // Hmmm, what about Non-menu containers?
  683.  
  684.             // Recursive call for the Menu case
  685.             if (comp instanceof JMenu) {
  686.                 JMenu subMenu = (JMenu) comp;
  687.                 if (subMenu.isMenuComponent(c))
  688.                     return true;
  689.             }
  690.         }
  691.         return false;
  692.     }
  693.  
  694.  
  695.     /*
  696.      * Returns a point in the coordinate space of this menu's popupmenu
  697.      * which corresponds to the point p in the menu's coordinate space.
  698.      *
  699.      * @param p the point to be translated
  700.      */
  701.     private Point translateToPopupMenu(Point p) {
  702.         return translateToPopupMenu(p.x, p.y);
  703.     }
  704.  
  705.     /*
  706.      * Returns a point in the coordinate space of this menu's popupmenu
  707.      * which corresponds to the point (x,y) in the menu's coordinate space.
  708.      * @param x the x coordinate of the point to be translated
  709.      * @param y the y coordinate of the point to be translated
  710.      */
  711.     private Point translateToPopupMenu(int x, int y) {
  712.             int newX;
  713.             int newY;
  714.  
  715.             if (getParent() instanceof JPopupMenu) {
  716.                 newX = x - getSize().width;
  717.                 newY = y;
  718.             } else {
  719.                 newX = x;
  720.                 newY = y - getSize().height;
  721.             }
  722.  
  723.             return new Point(newX, newY);
  724.         }
  725.  
  726.     /**
  727.      * Returns the popupmenu associated with this menu
  728.      */
  729.     public JPopupMenu getPopupMenu() {
  730.         ensurePopupMenuCreated();
  731.         return popupMenu;
  732.     }
  733.  
  734.     /**
  735.      * Add a listener for menu events
  736.      *
  737.      * @param l the listener to be added
  738.      */
  739.     public void addMenuListener(MenuListener l) {
  740.         listenerList.add(MenuListener.class, l);
  741.     }
  742.     
  743.     /**
  744.      * Remove a listener for menu events
  745.      *
  746.      * @param l the listener to be removed
  747.      */
  748.     public void removeMenuListener(MenuListener l) {
  749.         listenerList.remove(MenuListener.class, l);
  750.     }
  751.  
  752.     /**
  753.      * Notify all listeners that have registered interest for
  754.      * notification on this event type.  The event instance 
  755.      * is lazily created using the parameters passed into 
  756.      * the fire method.
  757.      *
  758.      * @see EventListenerList
  759.      */
  760.     protected void fireMenuSelected() {
  761.         // Guaranteed to return a non-null array
  762.         Object[] listeners = listenerList.getListenerList();
  763.         // Process the listeners last to first, notifying
  764.         // those that are interested in this event
  765.         for (int i = listeners.length-2; i>=0; i-=2) {
  766.             if (listeners[i]==MenuListener.class) {
  767.                 if (listeners[i+1]== null) {
  768.                     throw new Error(getText() +" has a NULL Listener!! " 
  769.                                        + i);
  770.                 } else {
  771.                     // Lazily create the event:
  772.                     if (menuEvent == null)
  773.                         menuEvent = new MenuEvent(this);
  774.                     ((MenuListener)listeners[i+1]).menuSelected(menuEvent);
  775.                 }              
  776.             }
  777.         }
  778.     }
  779.  
  780.     /**
  781.      * Notify all listeners that have registered interest for
  782.      * notification on this event type.  The event instance 
  783.      * is lazily created using the parameters passed into 
  784.      * the fire method.
  785.      *
  786.      * @see EventListenerList
  787.      */
  788.     protected void fireMenuDeselected() {
  789.         // Guaranteed to return a non-null array
  790.         Object[] listeners = listenerList.getListenerList();
  791.         // Process the listeners last to first, notifying
  792.         // those that are interested in this event
  793.         for (int i = listeners.length-2; i>=0; i-=2) {
  794.             if (listeners[i]==MenuListener.class) {
  795.                 if (listeners[i+1]== null) {
  796.                     System.out.println(getText() +" has a NULL Listener!! " 
  797.                                        + i);
  798.                 } else {
  799.                     // Lazily create the event:
  800.                     if (menuEvent == null)
  801.                         menuEvent = new MenuEvent(this);
  802.                     ((MenuListener)listeners[i+1]).menuDeselected(menuEvent);
  803.                 }              
  804.             }
  805.         }
  806.     }
  807.  
  808.     class MenuChangeListener implements ChangeListener, Serializable {
  809.         boolean isSelected = false;
  810.         public void stateChanged(ChangeEvent e) {               
  811.             DefaultButtonModel model = (DefaultButtonModel) e.getSource();
  812.             boolean modelSelected = model.isSelected();
  813.             // System.out.println("Model selected = " + modelSelected +
  814.             // " isSelected = " + isSelected);
  815.             if (modelSelected != isSelected) {
  816.                 if (modelSelected == true) {
  817.                     fireMenuSelected();
  818.                 } else {
  819.                     fireMenuDeselected();
  820.                 }
  821.                 isSelected = modelSelected;
  822.             }
  823.         }
  824.     }
  825.  
  826.     private ChangeListener createMenuChangeListener() {
  827.         return new MenuChangeListener();
  828.     }
  829.  
  830.     /**
  831.      * Create a window-closing listener for the popup.
  832.      *
  833.      * @param p the JPopupMenu
  834.      * @see WinListener
  835.      */
  836.     protected WinListener createWinListener(JPopupMenu p) {
  837.         return new WinListener(p);
  838.     }
  839.  
  840.     /**
  841.      * A listener class that watches for a popup window closing.
  842.      * When the popup is closing, the listener deselects the menu.
  843.      * <p>
  844.      * Warning: serialized objects of this class will not be compatible with
  845.      * future swing releases.  The current serialization support is appropriate
  846.      * for short term storage or RMI between Swing1.0 applications.  It will
  847.      * not be possible to load serialized Swing1.0 objects with future releases
  848.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  849.      * baseline for the serialized form of Swing objects.
  850.      */
  851.     protected class WinListener extends WindowAdapter implements Serializable {
  852.         JPopupMenu popupMenu;
  853.         /**
  854.          *  Create the window listener for the specified popup.
  855.          */
  856.         public WinListener(JPopupMenu p) {
  857.             this.popupMenu = p;
  858.         }
  859.         /**
  860.          * Deselect the menu when the popup is closed from outside.
  861.          */
  862.         public void windowClosing(WindowEvent e) {
  863.             setSelected(false);
  864.         }
  865.     }
  866.  
  867.     /**
  868.      * Process a mouse event. event is a MouseEvent with source being the receiving component.
  869.      * componentPath is the path of the receiving MenuComponent in the menu
  870.      * hierarchy. manager is the MenuSelectionManager for the menu hierarchy.
  871.      * This method should process the MouseEvent and change the menu selection if necessary
  872.      * by using MenuSelectionManager's API
  873.      * Note: you do not have to forward the event to sub-components. This is done automatically
  874.      * by the MenuSelectionManager
  875.      */
  876.     public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {
  877.         ((MenuUI)getUI()).processMouseEvent(this,event,path,manager);
  878.     }
  879.  
  880.     /** Implemented to be a MenuElement. This message is forwarded to the UI **/
  881.     public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
  882.         ((MenuUI)getUI()).processKeyEvent(this,e,path,manager);
  883.     }
  884.  
  885.     /**
  886.      *
  887.      */
  888.     public void menuSelectionChanged(boolean isIncluded) {
  889.         setSelected(isIncluded);
  890.     }
  891.  
  892.     /**
  893.      * This method should return an array containing the sub-menu components for this menu component
  894.      */
  895.     public MenuElement[] getSubElements() {
  896.         if(popupMenu == null)
  897.             return new MenuElement[0];
  898.         else {
  899.             MenuElement result[] = new MenuElement[1];
  900.             result[0] = popupMenu;
  901.             return result;
  902.         }
  903.     }
  904.  
  905.     
  906.     /*
  907.      * This method should return the java.awt.Component used to paint this MenuElement.
  908.      * The returned component will be used to convert events and detect if an event is inside
  909.      * a menu component.
  910.      */
  911.     public Component getComponent() {
  912.         return this;
  913.     }
  914.  
  915.  
  916.     /* 
  917.      * setAccelerator() is not defined for JMenu.  Use setMnemonic() instead. 
  918.      *
  919.      * @beaninfo
  920.      *     description: The keystroke combination which will invoke the JMenuItem's
  921.      *                  actionlisteners without navigating the menu hierarchy
  922.      *          hidden: true
  923.      */
  924.     public void setAccelerator(KeyStroke keyStroke) {
  925.     throw new Error("setAccelerator() is not defined for JMenu.  Use setMnemonic() instead.");
  926.     }
  927.  
  928.     /**
  929.      *
  930.      */
  931.     protected void processKeyEvent(KeyEvent e) {
  932.         MenuSelectionManager.defaultManager().processKeyEvent(e);
  933.         if(e.isConsumed())
  934.             return;
  935.         super.processKeyEvent(e);
  936.     }
  937. /////////////////
  938. // Accessibility support
  939. ////////////////
  940.  
  941.     /**
  942.      * Get the AccessibleContext associated with this JComponent
  943.      *
  944.      * @return the AccessibleContext of this JComponent
  945.      */
  946.     public AccessibleContext getAccessibleContext() {
  947.         if (accessibleContext == null) {
  948.             accessibleContext = new AccessibleJMenu();
  949.         }
  950.         return accessibleContext;
  951.     }
  952.  
  953.     /**
  954.      * The class used to obtain the accessible role for this object.
  955.      * <p>
  956.      * Warning: serialized objects of this class will not be compatible with
  957.      * future swing releases.  The current serialization support is appropriate
  958.      * for short term storage or RMI between Swing1.0 applications.  It will
  959.      * not be possible to load serialized Swing1.0 objects with future releases
  960.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  961.      * baseline for the serialized form of Swing objects.
  962.      */
  963.     protected class AccessibleJMenu extends AccessibleJMenuItem {
  964.         /**
  965.          * Returns the number of accessible children in the object.  If all
  966.          * of the children of this object implement Accessible, than this
  967.          * method should return the number of children of this object.
  968.          *
  969.          * @return the number of accessible children in the object.
  970.          */
  971.         public int getAccessibleChildrenCount() {
  972.             Component[] children = getMenuComponents();
  973.             int count = 0;
  974.             for (int j = 0; j < children.length; j++) {
  975.                 if (children[j] instanceof Accessible) {
  976.                     count++;
  977.                 }
  978.             }
  979.             return count;
  980.         }
  981.  
  982.         /**
  983.          * Return the nth Accessible child of the object.  
  984.          *
  985.          * @param i zero-based index of child
  986.          * @return the nth Accessible child of the object
  987.          */
  988.         public Accessible getAccessibleChild(int i) {
  989.             Component[] children = getMenuComponents();
  990.             int count = 0;
  991.             for (int j = 0; j < children.length; j++) {
  992.                 if (children[j] instanceof Accessible) {
  993.                     if (count == i) {
  994.                         if (children[j] instanceof JComponent) {
  995.                             // FIXME:  [[[WDW - probably should set this when
  996.                             // the component is added to the menu.  I tried
  997.                             // to do this in most cases, but the separators
  998.                             // added by addSeparator are hard to get to.]]]
  999.                             AccessibleContext ac = ((Accessible) children[j]).getAccessibleContext();
  1000.                             ac.setAccessibleParent(JMenu.this);
  1001.                         }
  1002.                         return (Accessible) children[j];
  1003.                     } else {
  1004.                         count++;
  1005.                     }
  1006.                 }
  1007.             }
  1008.             return null;
  1009.         }
  1010.  
  1011.         /**
  1012.          * Get the role of this object.
  1013.          *
  1014.          * @return an instance of AccessibleRole describing the role of the 
  1015.          * object
  1016.          * @see AccessibleRole
  1017.          */
  1018.         public AccessibleRole getAccessibleRole() {
  1019.             return AccessibleRole.MENU;
  1020.         }
  1021.     } // inner class AccessibleJMenu
  1022. }
  1023.  
  1024.