/*
   Source code by David Eck
                  Department of Mathematics and Computer Science
                  Hobart and William Smith Colleges
                  Geneva, NY 14456
                  eck@hws.edu
                  
   This Java source code file can be used IN UNMODIFIED FORM for
   any purpose.  You can also make and distribute modified versions,
   as long as you include an acknowledgement of the original
   author in the modified version.
*/

/**
 *  I've written this class to provide menus that work more
 *  like menus I am used to from Macintosh programming.  An xMenu has
 *  a function, doCommand, that is called when the user selects
 *  an item from the menu.  (This works only for items that
 *  are added with the various "add" methods that are defined
 *  in this class, not with the ones inherited from the JMenu
 *  class.)  It also has an "update" function that
 *  is called just before the menu is about to appear.
 *  The "doCommand" and "update" method sare  empty.  They
 *  should ordinarily be defined in a subclass (unless they
 *  have nothing to do).
 *  <p>A few other utility routines are provided for manipulating
 *  items in the menu, such as enabling, disabling, setting the
 *  text of the item, and checking/changing the state of check boxes
 *  and radio group items.
 */

package xca;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class xMenu extends JMenu implements ActionListener, PopupMenuListener {

   //-------------- Listener methods -----------------------
   public void actionPerformed(ActionEvent evt) { 
      Object src = evt.getSource();
      int ct = getMenuComponentCount();
      for (int i = 0; i < ct; i++) {
         if (getMenuComponent(i) instanceof JMenu) {  
            JMenu m = (JMenu)getMenuComponent(i);
            int mct = m.getMenuComponentCount();
            for (int j = 0; j < mct; j++) {
               if (m.getMenuComponent(j) == src) {
                  doSubmenuCommand(i+1,j+1);
                  return;
               }
            }
         }
         else if (getMenuComponent(i) == src) {
            doCommand(i+1);
            return;  
         }
      }
   }
   public void popupMenuCanceled(PopupMenuEvent evt) {
   }
   public void popupMenuWillBecomeInvisible(PopupMenuEvent evt) {
   }
   public void popupMenuWillBecomeVisible(PopupMenuEvent evt) {
      update();
   }
   //----------------------------------------------------

   /**
    * Create a menu with the specified title, which will
    * appear in the menu bar, if this is a top-level menu,
    * or as the name of the item in the parent menu, if this
    * is a sub-menu.  Note:  xMenu's are implemented using
    * "heavyweight" components, which is not the defalt for
    * JMenu's.  This is to avoid flickering when the menu
    * appears over an animated window that has double-buffering
    * disabled.
    */
   public xMenu(String title) {
      super(title);
      getPopupMenu().addPopupMenuListener(this);
//      getPopupMenu().setLightWeightPopupEnabled(false);
   }
   
   /**
    *  This is called when the user selects an item from the
    *  menu.  The parameter is the number of the item in the 
    *  menu, where the number of the first item is 1 (NOT ZERO!).
    *  (1-based numbering is used for campatibility with Macintosh menus.)
    *  The version in this class does nothing.
    */
   protected void doCommand(int itemNum) {
   }
   
   /**
    *  If a submenu has been added to the xMenu USING THE
    *  addSubmenu METHOD IN THIS CLASS, then this method is
    *  called when the user selects an item from that
    *  menu.  The first parameter is the item number of the
    *  submenu in the main menu.  The second parameter  is the
    *  item number of the selected command in the submenu.
    *  Both item numbers are 1-bases, not 0-based.  The 
    *  version in this class does nothing.
    */
   protected void doSubmenuCommand(int itemInMainMenu, int itemInSubMenu) {
   }
   
   /**
    * This is called just before the menu is to be popped up
    * on the screen.  The idea is to give you a chance to make
    * any necessary changes to the appearance of the menu, such
    * as enabling and disabling menu items.  (Note that it is
    * NOT called before accelerators are processed!  You can call
    * it programmatically, if your program depends on changes made
    * here in the state of the menu.)
    */
   public void update() {
   }
   
   /**
    * Create a basic item and add it to the menu.
    * Returns the JMenuItem that is created.
    */
   public JMenuItem add(String itemName) {
      JMenuItem item = super.add(itemName);
      item.addActionListener(this);  
      return item;
   }
   
   /**
    * Creates a JMenu (not xMenu) and adds it to this xMenu
    * as a submenu.  The first parameter is the text that
    * appears in this xMenu.  The second paramter is a non-empty
    * list of strings that become the items in the submenu.
    * When the user choses an item from the submenu, the
    * method doSubmenCommand in this class is called.
    * To add a separator to the submenu, use a null in
    * in the list of item names.
    */
   public JMenu addSubmenu(String title, String[] itemNames) {
      JMenu menu = new JMenu(title);
      for (int i = 0; i < itemNames.length; i++)
         if (itemNames[i] == null)
            menu.addSeparator();
         else
            menu.add(itemNames[i]).addActionListener(this);
      menu.getPopupMenu().setLightWeightPopupEnabled(false);
      add(menu);
      return menu;
   }
   
   /**
    * Create one item in this xMenu for each string in the array.
    */
   public void add(String[] itemNames) {
      if (itemNames != null)
         for (int i = 0; i < itemNames.length; i++)
            add(itemNames[i]);
   }
   
   /**
    *  Create an item with an accelerator.
    *  @param itemName The text that shows in the menu.
    *  @param acceleratorDescription A string that specifies
    *     the accelerator using the syntax from the class
    *     javax.swing.KeyStroke.  For exhibit:  "control A",
    *     "alt shift X", "shift INSERT".
    */
   public void add(String itemName, String acceleratorDescription) {
      JMenuItem item = super.add(itemName);
      item.addActionListener(this);  
      item.setAccelerator(KeyStroke.getKeyStroke(acceleratorDescription));
   }
   
   /**
    *  Adds a JCheckBoxMenuItem to the menu, and returns it.
    *  The menu item will trigger a call to doCommand when the
    *  user selects it.  (Checking and unchecking is taken care
    *  of automatically.)
    */
   public JCheckBoxMenuItem addCheckItem(String itemName) {
      JCheckBoxMenuItem item = new JCheckBoxMenuItem(itemName);
      add(item);
      item.addActionListener(this);
      return item;
   }
   
   /**
    * Adds an entire radio group to the menu, with one
    * JRadioButtonMenuItem for each string in the array.
    * Each menu item will trigger a call to doCommand when
    * the user selects it.  (Checking and unchecking is 
    * taken care of automatically.)  The second parameter
    * gives the zero-based index of the initially selected item.
    * If it has an illegal value, such as -1, then no
    * item is initially selected.  The return value is
    * the ButtonGroup that controls the selection.
    */
   public ButtonGroup addRadioGroup(String[] itemNames, int selected) {
      ButtonGroup group = new ButtonGroup();
      if (itemNames != null) {
         for (int i = 0; i < itemNames.length; i++) {
            JRadioButtonMenuItem item = new JRadioButtonMenuItem(itemNames[i]);
            add(item);
            item.addActionListener(this);
            group.add(item);
            if (i == selected)
               item.setSelected(true);
         }
      }
      return group;
   }
   
   /**
    * Set the item name displayed for a menu item.  The item
    * number for the first item in the menu is 1, NOT ZERO.
    */
   public void setItemName(int itemNum, String itemName) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JMenuItem)
           ((JMenuItem)c).setText(itemName);
      }
   }
   
   /**
    * Gets the item name displayed for a menu item.  The item
    * number for the first item in the menu is 1, NOT ZERO.
    * If there is no such item, null is returned.
    */
   public String getItemName(int itemNum) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JMenuItem)
           return ((JMenuItem)c).getText();
      }
      return null;
   }
   
   /**
    * Enable a specified menu item.  The item
    * number for the first item in the menu is 1, NOT ZERO.
    */
   public void enableItem(int itemNum) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JMenuItem)
           ((JMenuItem)c).setEnabled(true);
      }
   }
   
   /**
    * Disable a specified menu item.  The item
    * number for the first item in the menu is 1, NOT ZERO.
    */
   public void disableItem(int itemNum) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JMenuItem)
           ((JMenuItem)c).setEnabled(false);
      }
   }
   
   /**
    * Set the check box state of a JCheckBoxMenuItem.  
    * The item number for the first item in the menu is 1, NOT ZERO.
    * If the item number in the check box is not a JCheckBoxMenuItem,
    * then the method call is ignored.
    */
   public void setChecked(int itemNum, boolean checked) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JCheckBoxMenuItem)
           ((JCheckBoxMenuItem)c).setState(checked);
      }
   }
   
   /**
    * Get the check box state of a JCheckBoxMenuItem.  
    * The item number for the first item in the menu is 1, NOT ZERO.
    * (If the item number in the check box is not a JCheckBoxMenuItem,
    * then the return value if false.)
    */
   public boolean getChecked(int itemNum) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JCheckBoxMenuItem)
           return ((JCheckBoxMenuItem)c).getState();
      }
      return false;
   }
   
   /**
    *  Select one of a group of JRadioButtonMenuItems.  The item
    *  number for the first item in the menu is 1, NOT ZERO.
    *  If the item number does not correspond to a 
    *  JRadioButtonMenuItem, the return value is false.
    */
   public boolean getRadioState(int itemNum) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JRadioButtonMenuItem)
           return ((JRadioButtonMenuItem)c).isSelected();
      }
      return false;
   }

   /**
    *  Check the state of one of a group of JRadioButtonMenuItems.  
    *  The item number for the first item in the menu is 1, NOT ZERO.
    *  If the item number does not correspond to a 
    *  JRadioButtonMenuItem, the method call is ignored.
    *  (Note that the currently selected item in the group, if 
    *  any, is automatically de-selected.)
    */
   public void selectRadioItem(int itemNum) {
      int ct = getMenuComponentCount();
      itemNum--;  // Change from 1-based to 0-based numbering.
      if (itemNum >= 0 && itemNum < ct) {
         Component c = getMenuComponent(itemNum);
         if (c != null && c instanceof JRadioButtonMenuItem)
           ((JRadioButtonMenuItem)c).setSelected(true);
      }
   }

   
   
   
}

