Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]
    1   /*
    2    * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.swing.plaf.basic;
   27   
   28   import javax.swing;
   29   import javax.swing.event;
   30   import javax.swing.plaf;
   31   import javax.swing.plaf.basic;
   32   import javax.swing.border;
   33   
   34   import java.applet.Applet;
   35   
   36   import java.awt.Component;
   37   import java.awt.Container;
   38   import java.awt.Dimension;
   39   import java.awt.KeyboardFocusManager;
   40   import java.awt.Window;
   41   import java.awt.event;
   42   import java.awt.AWTEvent;
   43   import java.awt.Toolkit;
   44   
   45   import java.beans.PropertyChangeListener;
   46   import java.beans.PropertyChangeEvent;
   47   
   48   import java.util;
   49   
   50   import sun.swing.DefaultLookup;
   51   import sun.swing.UIAction;
   52   
   53   import sun.awt.AppContext;
   54   
   55   /**
   56    * A Windows L&F implementation of PopupMenuUI.  This implementation
   57    * is a "combined" view/controller.
   58    *
   59    * @author Georges Saab
   60    * @author David Karlton
   61    * @author Arnaud Weber
   62    */
   63   public class BasicPopupMenuUI extends PopupMenuUI {
   64       static final StringBuilder MOUSE_GRABBER_KEY = new StringBuilder(
   65                      "javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber");
   66       static final StringBuilder MENU_KEYBOARD_HELPER_KEY = new StringBuilder(
   67                      "javax.swing.plaf.basic.BasicPopupMenuUI.MenuKeyboardHelper");
   68   
   69       protected JPopupMenu popupMenu = null;
   70       private transient PopupMenuListener popupMenuListener = null;
   71       private MenuKeyListener menuKeyListener = null;
   72   
   73       private static boolean checkedUnpostPopup;
   74       private static boolean unpostPopup;
   75   
   76       public static ComponentUI createUI(JComponent x) {
   77           return new BasicPopupMenuUI();
   78       }
   79   
   80       public BasicPopupMenuUI() {
   81           BasicLookAndFeel.needsEventHelper = true;
   82           LookAndFeel laf = UIManager.getLookAndFeel();
   83           if (laf instanceof BasicLookAndFeel) {
   84               ((BasicLookAndFeel)laf).installAWTEventListener();
   85           }
   86       }
   87   
   88       public void installUI(JComponent c) {
   89           popupMenu = (JPopupMenu) c;
   90   
   91           installDefaults();
   92           installListeners();
   93           installKeyboardActions();
   94       }
   95   
   96       public void installDefaults() {
   97           if (popupMenu.getLayout() == null ||
   98               popupMenu.getLayout() instanceof UIResource)
   99               popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));
  100   
  101           LookAndFeel.installProperty(popupMenu, "opaque", Boolean.TRUE);
  102           LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
  103           LookAndFeel.installColorsAndFont(popupMenu,
  104                                            "PopupMenu.background",
  105                                            "PopupMenu.foreground",
  106                                            "PopupMenu.font");
  107       }
  108   
  109       protected void installListeners() {
  110           if (popupMenuListener == null) {
  111               popupMenuListener = new BasicPopupMenuListener();
  112           }
  113           popupMenu.addPopupMenuListener(popupMenuListener);
  114   
  115           if (menuKeyListener == null) {
  116               menuKeyListener = new BasicMenuKeyListener();
  117           }
  118           popupMenu.addMenuKeyListener(menuKeyListener);
  119   
  120           AppContext context = AppContext.getAppContext();
  121           synchronized (MOUSE_GRABBER_KEY) {
  122               MouseGrabber mouseGrabber = (MouseGrabber)context.get(
  123                                                        MOUSE_GRABBER_KEY);
  124               if (mouseGrabber == null) {
  125                   mouseGrabber = new MouseGrabber();
  126                   context.put(MOUSE_GRABBER_KEY, mouseGrabber);
  127               }
  128           }
  129           synchronized (MENU_KEYBOARD_HELPER_KEY) {
  130               MenuKeyboardHelper helper =
  131                       (MenuKeyboardHelper)context.get(MENU_KEYBOARD_HELPER_KEY);
  132               if (helper == null) {
  133                   helper = new MenuKeyboardHelper();
  134                   context.put(MENU_KEYBOARD_HELPER_KEY, helper);
  135                   MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  136                   msm.addChangeListener(helper);
  137               }
  138           }
  139       }
  140   
  141       protected void installKeyboardActions() {
  142       }
  143   
  144       static InputMap getInputMap(JPopupMenu popup, JComponent c) {
  145           InputMap windowInputMap = null;
  146           Object[] bindings = (Object[])UIManager.get("PopupMenu.selectedWindowInputMapBindings");
  147           if (bindings != null) {
  148               windowInputMap = LookAndFeel.makeComponentInputMap(c, bindings);
  149               if (!popup.getComponentOrientation().isLeftToRight()) {
  150                   Object[] km = (Object[])UIManager.get("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
  151                   if (km != null) {
  152                       InputMap rightToLeftInputMap = LookAndFeel.makeComponentInputMap(c, km);
  153                       rightToLeftInputMap.setParent(windowInputMap);
  154                       windowInputMap = rightToLeftInputMap;
  155                   }
  156               }
  157           }
  158           return windowInputMap;
  159       }
  160   
  161       static ActionMap getActionMap() {
  162           return LazyActionMap.getActionMap(BasicPopupMenuUI.class,
  163                                             "PopupMenu.actionMap");
  164       }
  165   
  166       static void loadActionMap(LazyActionMap map) {
  167           map.put(new Actions(Actions.CANCEL));
  168           map.put(new Actions(Actions.SELECT_NEXT));
  169           map.put(new Actions(Actions.SELECT_PREVIOUS));
  170           map.put(new Actions(Actions.SELECT_PARENT));
  171           map.put(new Actions(Actions.SELECT_CHILD));
  172           map.put(new Actions(Actions.RETURN));
  173           BasicLookAndFeel.installAudioActionMap(map);
  174       }
  175   
  176       public void uninstallUI(JComponent c) {
  177           uninstallDefaults();
  178           uninstallListeners();
  179           uninstallKeyboardActions();
  180   
  181           popupMenu = null;
  182       }
  183   
  184       protected void uninstallDefaults() {
  185           LookAndFeel.uninstallBorder(popupMenu);
  186       }
  187   
  188       protected void uninstallListeners() {
  189           if (popupMenuListener != null) {
  190               popupMenu.removePopupMenuListener(popupMenuListener);
  191           }
  192           if (menuKeyListener != null) {
  193               popupMenu.removeMenuKeyListener(menuKeyListener);
  194           }
  195       }
  196   
  197       protected void uninstallKeyboardActions() {
  198           SwingUtilities.replaceUIActionMap(popupMenu, null);
  199           SwingUtilities.replaceUIInputMap(popupMenu,
  200                                     JComponent.WHEN_IN_FOCUSED_WINDOW, null);
  201       }
  202   
  203       static MenuElement getFirstPopup() {
  204           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  205           MenuElement[] p = msm.getSelectedPath();
  206           MenuElement me = null;
  207   
  208           for(int i = 0 ; me == null && i < p.length ; i++) {
  209               if (p[i] instanceof JPopupMenu)
  210                   me = p[i];
  211           }
  212   
  213           return me;
  214       }
  215   
  216       static JPopupMenu getLastPopup() {
  217           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  218           MenuElement[] p = msm.getSelectedPath();
  219           JPopupMenu popup = null;
  220   
  221           for(int i = p.length - 1; popup == null && i >= 0; i--) {
  222               if (p[i] instanceof JPopupMenu)
  223                   popup = (JPopupMenu)p[i];
  224           }
  225           return popup;
  226       }
  227   
  228       static List getPopups() {
  229           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  230           MenuElement[] p = msm.getSelectedPath();
  231   
  232           List list = new ArrayList(p.length);
  233           for(int i = 0; i < p.length; i++) {
  234               if (p[i] instanceof JPopupMenu) {
  235                   list.add((JPopupMenu)p[i]);
  236               }
  237           }
  238           return list;
  239       }
  240   
  241       public boolean isPopupTrigger(MouseEvent e) {
  242           return ((e.getID()==MouseEvent.MOUSE_RELEASED)
  243                   && ((e.getModifiers() & MouseEvent.BUTTON3_MASK)!=0));
  244       }
  245   
  246       private static boolean checkInvokerEqual(MenuElement present, MenuElement last) {
  247           Component invokerPresent = present.getComponent();
  248           Component invokerLast = last.getComponent();
  249   
  250           if (invokerPresent instanceof JPopupMenu) {
  251               invokerPresent = ((JPopupMenu)invokerPresent).getInvoker();
  252       }
  253           if (invokerLast instanceof JPopupMenu) {
  254               invokerLast = ((JPopupMenu)invokerLast).getInvoker();
  255           }
  256           return (invokerPresent == invokerLast);
  257       }
  258   
  259   
  260       /**
  261        * This Listener fires the Action that provides the correct auditory
  262        * feedback.
  263        *
  264        * @since 1.4
  265        */
  266       private class BasicPopupMenuListener implements PopupMenuListener {
  267           public void popupMenuCanceled(PopupMenuEvent e) {
  268           }
  269   
  270           public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
  271           }
  272   
  273           public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
  274               BasicLookAndFeel.playSound((JPopupMenu)e.getSource(),
  275                                          "PopupMenu.popupSound");
  276           }
  277       }
  278   
  279       /**
  280        * Handles mnemonic for children JMenuItems.
  281        * @since 1.5
  282        */
  283       private class BasicMenuKeyListener implements MenuKeyListener {
  284           MenuElement menuToOpen = null;
  285   
  286           public void menuKeyTyped(MenuKeyEvent e) {
  287               if (menuToOpen != null) {
  288                   // we have a submenu to open
  289                   JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu();
  290                   MenuElement subitem = findEnabledChild(
  291                           subpopup.getSubElements(), -1, true);
  292   
  293                   ArrayList lst = new ArrayList(Arrays.asList(e.getPath()));
  294                   lst.add(menuToOpen);
  295                   lst.add(subpopup);
  296                   if (subitem != null) {
  297                       lst.add(subitem);
  298                   }
  299                   MenuElement newPath[] = new MenuElement[0];;
  300                   newPath = (MenuElement[])lst.toArray(newPath);
  301                   MenuSelectionManager.defaultManager().setSelectedPath(newPath);
  302                   e.consume();
  303               }
  304               menuToOpen = null;
  305           }
  306   
  307           public void menuKeyPressed(MenuKeyEvent e) {
  308               char keyChar = e.getKeyChar();
  309   
  310               // Handle the case for Escape or Enter...
  311               if (!Character.isLetterOrDigit(keyChar)) {
  312                   return;
  313               }
  314   
  315               MenuSelectionManager manager = e.getMenuSelectionManager();
  316               MenuElement path[] = e.getPath();
  317               MenuElement items[] = popupMenu.getSubElements();
  318               int currentIndex = -1;
  319               int matches = 0;
  320               int firstMatch = -1;
  321               int indexes[] = null;
  322   
  323               for (int j = 0; j < items.length; j++) {
  324                   if (! (items[j] instanceof JMenuItem)) {
  325                       continue;
  326                   }
  327                   JMenuItem item = (JMenuItem)items[j];
  328                   int mnemonic = item.getMnemonic();
  329                   if (item.isEnabled() &&
  330                       item.isVisible() && lower(keyChar) == lower(mnemonic)) {
  331                       if (matches == 0) {
  332                           firstMatch = j;
  333                           matches++;
  334                       } else {
  335                           if (indexes == null) {
  336                               indexes = new int[items.length];
  337                               indexes[0] = firstMatch;
  338                           }
  339                           indexes[matches++] = j;
  340                       }
  341                   }
  342                   if (item.isArmed()) {
  343                       currentIndex = matches - 1;
  344                   }
  345               }
  346   
  347               if (matches == 0) {
  348                   ; // no op
  349               } else if (matches == 1) {
  350                   // Invoke the menu action
  351                   JMenuItem item = (JMenuItem)items[firstMatch];
  352                   if (item instanceof JMenu) {
  353                       // submenus are handled in menuKeyTyped
  354                       menuToOpen = item;
  355                   } else if (item.isEnabled()) {
  356                       // we have a menu item
  357                       manager.clearSelectedPath();
  358                       item.doClick();
  359                   }
  360                   e.consume();
  361               } else {
  362                   // Select the menu item with the matching mnemonic. If
  363                   // the same mnemonic has been invoked then select the next
  364                   // menu item in the cycle.
  365                   MenuElement newItem = null;
  366   
  367                   newItem = items[indexes[(currentIndex + 1) % matches]];
  368   
  369                   MenuElement newPath[] = new MenuElement[path.length+1];
  370                   System.arraycopy(path, 0, newPath, 0, path.length);
  371                   newPath[path.length] = newItem;
  372                   manager.setSelectedPath(newPath);
  373                   e.consume();
  374               }
  375               return;
  376           }
  377   
  378           public void menuKeyReleased(MenuKeyEvent e) {
  379           }
  380   
  381           private char lower(char keyChar) {
  382               return Character.toLowerCase(keyChar);
  383           }
  384   
  385           private char lower(int mnemonic) {
  386               return Character.toLowerCase((char) mnemonic);
  387           }
  388       }
  389   
  390       private static class Actions extends UIAction {
  391           // Types of actions
  392           private static final String CANCEL = "cancel";
  393           private static final String SELECT_NEXT = "selectNext";
  394           private static final String SELECT_PREVIOUS = "selectPrevious";
  395           private static final String SELECT_PARENT = "selectParent";
  396           private static final String SELECT_CHILD = "selectChild";
  397           private static final String RETURN = "return";
  398   
  399           // Used for next/previous actions
  400           private static final boolean FORWARD = true;
  401           private static final boolean BACKWARD = false;
  402   
  403           // Used for parent/child actions
  404           private static final boolean PARENT = false;
  405           private static final boolean CHILD = true;
  406   
  407   
  408           Actions(String key) {
  409               super(key);
  410           }
  411   
  412           public void actionPerformed(ActionEvent e) {
  413               String key = getName();
  414               if (key == CANCEL) {
  415                   cancel();
  416               }
  417               else if (key == SELECT_NEXT) {
  418                   selectItem(FORWARD);
  419               }
  420               else if (key == SELECT_PREVIOUS) {
  421                   selectItem(BACKWARD);
  422               }
  423               else if (key == SELECT_PARENT) {
  424                   selectParentChild(PARENT);
  425               }
  426               else if (key == SELECT_CHILD) {
  427                   selectParentChild(CHILD);
  428               }
  429               else if (key == RETURN) {
  430                   doReturn();
  431               }
  432           }
  433   
  434           private void doReturn() {
  435               KeyboardFocusManager fmgr =
  436                   KeyboardFocusManager.getCurrentKeyboardFocusManager();
  437               Component focusOwner = fmgr.getFocusOwner();
  438               if(focusOwner != null && !(focusOwner instanceof JRootPane)) {
  439                   return;
  440               }
  441   
  442               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  443               MenuElement path[] = msm.getSelectedPath();
  444               MenuElement lastElement;
  445               if(path.length > 0) {
  446                   lastElement = path[path.length-1];
  447                   if(lastElement instanceof JMenu) {
  448                       MenuElement newPath[] = new MenuElement[path.length+1];
  449                       System.arraycopy(path,0,newPath,0,path.length);
  450                       newPath[path.length] = ((JMenu)lastElement).getPopupMenu();
  451                       msm.setSelectedPath(newPath);
  452                   } else if(lastElement instanceof JMenuItem) {
  453                       JMenuItem mi = (JMenuItem)lastElement;
  454   
  455                       if (mi.getUI() instanceof BasicMenuItemUI) {
  456                           ((BasicMenuItemUI)mi.getUI()).doClick(msm);
  457                       }
  458                       else {
  459                           msm.clearSelectedPath();
  460                           mi.doClick(0);
  461                       }
  462                   }
  463               }
  464           }
  465           private void selectParentChild(boolean direction) {
  466               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  467               MenuElement path[] = msm.getSelectedPath();
  468               int len = path.length;
  469   
  470               if (direction == PARENT) {
  471                   // selecting parent
  472                   int popupIndex = len-1;
  473   
  474                   if (len > 2 &&
  475                       // check if we have an open submenu. A submenu item may or
  476                       // may not be selected, so submenu popup can be either the
  477                       // last or next to the last item.
  478                       (path[popupIndex] instanceof JPopupMenu ||
  479                        path[--popupIndex] instanceof JPopupMenu) &&
  480                       !((JMenu)path[popupIndex-1]).isTopLevelMenu()) {
  481   
  482                       // we have a submenu, just close it
  483                       MenuElement newPath[] = new MenuElement[popupIndex];
  484                       System.arraycopy(path, 0, newPath, 0, popupIndex);
  485                       msm.setSelectedPath(newPath);
  486                       return;
  487                   }
  488               } else {
  489                   // selecting child
  490                   if (len > 0 && path[len-1] instanceof JMenu &&
  491                       !((JMenu)path[len-1]).isTopLevelMenu()) {
  492   
  493                       // we have a submenu, open it
  494                       JMenu menu = (JMenu)path[len-1];
  495                       JPopupMenu popup = menu.getPopupMenu();
  496                       MenuElement[] subs = popup.getSubElements();
  497                       MenuElement item = findEnabledChild(subs, -1, true);
  498                       MenuElement[] newPath;
  499   
  500                       if (item == null) {
  501                           newPath = new MenuElement[len+1];
  502                       } else {
  503                           newPath = new MenuElement[len+2];
  504                           newPath[len+1] = item;
  505                       }
  506                       System.arraycopy(path, 0, newPath, 0, len);
  507                       newPath[len] = popup;
  508                       msm.setSelectedPath(newPath);
  509                       return;
  510                   }
  511               }
  512   
  513               // check if we have a toplevel menu selected.
  514               // If this is the case, we select another toplevel menu
  515               if (len > 1 && path[0] instanceof JMenuBar) {
  516                   MenuElement currentMenu = path[1];
  517                   MenuElement nextMenu = findEnabledChild(
  518                       path[0].getSubElements(), currentMenu, direction);
  519   
  520                   if (nextMenu != null && nextMenu != currentMenu) {
  521                       MenuElement newSelection[];
  522                       if (len == 2) {
  523                           // menu is selected but its popup not shown
  524                           newSelection = new MenuElement[2];
  525                           newSelection[0] = path[0];
  526                           newSelection[1] = nextMenu;
  527                       } else {
  528                           // menu is selected and its popup is shown
  529                           newSelection = new MenuElement[3];
  530                           newSelection[0] = path[0];
  531                           newSelection[1] = nextMenu;
  532                           newSelection[2] = ((JMenu)nextMenu).getPopupMenu();
  533                       }
  534                       msm.setSelectedPath(newSelection);
  535                   }
  536               }
  537           }
  538   
  539           private void selectItem(boolean direction) {
  540               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  541               MenuElement path[] = msm.getSelectedPath();
  542               if (path.length == 0) {
  543                   return;
  544               }
  545               int len = path.length;
  546               if (len == 1 && path[0] instanceof JPopupMenu) {
  547   
  548                   JPopupMenu popup = (JPopupMenu) path[0];
  549                   MenuElement[] newPath = new MenuElement[2];
  550                   newPath[0] = popup;
  551                   newPath[1] = findEnabledChild(popup.getSubElements(), -1, direction);
  552                   msm.setSelectedPath(newPath);
  553               } else if (len == 2 &&
  554                       path[0] instanceof JMenuBar && path[1] instanceof JMenu) {
  555   
  556                   // a toplevel menu is selected, but its popup not shown.
  557                   // Show the popup and select the first item
  558                   JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
  559                   MenuElement next =
  560                       findEnabledChild(popup.getSubElements(), -1, FORWARD);
  561                   MenuElement[] newPath;
  562   
  563                   if (next != null) {
  564                       // an enabled item found -- include it in newPath
  565                       newPath = new MenuElement[4];
  566                       newPath[3] = next;
  567                   } else {
  568                       // menu has no enabled items -- still must show the popup
  569                       newPath = new MenuElement[3];
  570                   }
  571                   System.arraycopy(path, 0, newPath, 0, 2);
  572                   newPath[2] = popup;
  573                   msm.setSelectedPath(newPath);
  574   
  575               } else if (path[len-1] instanceof JPopupMenu &&
  576                          path[len-2] instanceof JMenu) {
  577   
  578                   // a menu (not necessarily toplevel) is open and its popup
  579                   // shown. Select the appropriate menu item
  580                   JMenu menu = (JMenu)path[len-2];
  581                   JPopupMenu popup = menu.getPopupMenu();
  582                   MenuElement next =
  583                       findEnabledChild(popup.getSubElements(), -1, direction);
  584   
  585                   if (next != null) {
  586                       MenuElement[] newPath = new MenuElement[len+1];
  587                       System.arraycopy(path, 0, newPath, 0, len);
  588                       newPath[len] = next;
  589                       msm.setSelectedPath(newPath);
  590                   } else {
  591                       // all items in the popup are disabled.
  592                       // We're going to find the parent popup menu and select
  593                       // its next item. If there's no parent popup menu (i.e.
  594                       // current menu is toplevel), do nothing
  595                       if (len > 2 && path[len-3] instanceof JPopupMenu) {
  596                           popup = ((JPopupMenu)path[len-3]);
  597                           next = findEnabledChild(popup.getSubElements(),
  598                                                   menu, direction);
  599   
  600                           if (next != null && next != menu) {
  601                               MenuElement[] newPath = new MenuElement[len-1];
  602                               System.arraycopy(path, 0, newPath, 0, len-2);
  603                               newPath[len-2] = next;
  604                               msm.setSelectedPath(newPath);
  605                           }
  606                       }
  607                   }
  608   
  609               } else {
  610                   // just select the next item, no path expansion needed
  611                   MenuElement subs[] = path[len-2].getSubElements();
  612                   MenuElement nextChild =
  613                       findEnabledChild(subs, path[len-1], direction);
  614                   if (nextChild == null) {
  615                       nextChild = findEnabledChild(subs, -1, direction);
  616                   }
  617                   if (nextChild != null) {
  618                       path[len-1] = nextChild;
  619                       msm.setSelectedPath(path);
  620                   }
  621               }
  622           }
  623   
  624           private void cancel() {
  625               // 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's
  626               // a protected method. The real solution could be to make
  627               // firePopupMenuCanceled public and call it directly.
  628               JPopupMenu lastPopup = (JPopupMenu)getLastPopup();
  629               if (lastPopup != null) {
  630                   lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
  631               }
  632               String mode = UIManager.getString("Menu.cancelMode");
  633               if ("hideMenuTree".equals(mode)) {
  634                   MenuSelectionManager.defaultManager().clearSelectedPath();
  635               } else {
  636                   shortenSelectedPath();
  637               }
  638           }
  639   
  640           private void shortenSelectedPath() {
  641               MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
  642               if (path.length <= 2) {
  643                   MenuSelectionManager.defaultManager().clearSelectedPath();
  644                   return;
  645               }
  646               // unselect MenuItem and its Popup by default
  647               int value = 2;
  648               MenuElement lastElement = path[path.length - 1];
  649               JPopupMenu lastPopup = getLastPopup();
  650               if (lastElement == lastPopup) {
  651                   MenuElement previousElement = path[path.length - 2];
  652                   if (previousElement instanceof JMenu) {
  653                       JMenu lastMenu = (JMenu) previousElement;
  654                       if (lastMenu.isEnabled() && lastPopup.getComponentCount() > 0) {
  655                           // unselect the last visible popup only
  656                           value = 1;
  657                       } else {
  658                           // unselect invisible popup and two visible elements
  659                           value = 3;
  660                       }
  661                   }
  662               }
  663               if (path.length - value <= 2
  664                       && !UIManager.getBoolean("Menu.preserveTopLevelSelection")) {
  665                   // clear selection for the topLevelMenu
  666                   value = path.length;
  667               }
  668               MenuElement newPath[] = new MenuElement[path.length - value];
  669               System.arraycopy(path, 0, newPath, 0, path.length - value);
  670               MenuSelectionManager.defaultManager().setSelectedPath(newPath);
  671           }
  672       }
  673   
  674       private static MenuElement nextEnabledChild(MenuElement e[],
  675                                                   int fromIndex, int toIndex) {
  676           for (int i=fromIndex; i<=toIndex; i++) {
  677               if (e[i] != null) {
  678                   Component comp = e[i].getComponent();
  679                   if ( comp != null
  680                           && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
  681                           && comp.isVisible()) {
  682                       return e[i];
  683                   }
  684               }
  685           }
  686           return null;
  687       }
  688   
  689       private static MenuElement previousEnabledChild(MenuElement e[],
  690                                                   int fromIndex, int toIndex) {
  691           for (int i=fromIndex; i>=toIndex; i--) {
  692               if (e[i] != null) {
  693                   Component comp = e[i].getComponent();
  694                   if ( comp != null
  695                           && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
  696                           && comp.isVisible()) {
  697                       return e[i];
  698                   }
  699               }
  700           }
  701           return null;
  702       }
  703   
  704       static MenuElement findEnabledChild(MenuElement e[], int fromIndex,
  705                                                   boolean forward) {
  706           MenuElement result = null;
  707           if (forward) {
  708               result = nextEnabledChild(e, fromIndex+1, e.length-1);
  709               if (result == null) result = nextEnabledChild(e, 0, fromIndex-1);
  710           } else {
  711               result = previousEnabledChild(e, fromIndex-1, 0);
  712               if (result == null) result = previousEnabledChild(e, e.length-1,
  713                                                                 fromIndex+1);
  714           }
  715           return result;
  716       }
  717   
  718       static MenuElement findEnabledChild(MenuElement e[],
  719                                      MenuElement elem, boolean forward) {
  720           for (int i=0; i<e.length; i++) {
  721               if (e[i] == elem) {
  722                   return findEnabledChild(e, i, forward);
  723               }
  724           }
  725           return null;
  726       }
  727   
  728       static class MouseGrabber implements ChangeListener,
  729           AWTEventListener, ComponentListener, WindowListener {
  730   
  731           Window grabbedWindow;
  732           MenuElement[] lastPathSelected;
  733   
  734           public MouseGrabber() {
  735               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  736               msm.addChangeListener(this);
  737               this.lastPathSelected = msm.getSelectedPath();
  738               if(this.lastPathSelected.length != 0) {
  739                   grabWindow(this.lastPathSelected);
  740               }
  741           }
  742   
  743           void uninstall() {
  744               synchronized (MOUSE_GRABBER_KEY) {
  745                   MenuSelectionManager.defaultManager().removeChangeListener(this);
  746                   ungrabWindow();
  747                   AppContext.getAppContext().remove(MOUSE_GRABBER_KEY);
  748               }
  749           }
  750   
  751           void grabWindow(MenuElement[] newPath) {
  752               // A grab needs to be added
  753               final Toolkit tk = Toolkit.getDefaultToolkit();
  754               java.security.AccessController.doPrivileged(
  755                   new java.security.PrivilegedAction() {
  756                       public Object run() {
  757                           tk.addAWTEventListener(MouseGrabber.this,
  758                                   AWTEvent.MOUSE_EVENT_MASK |
  759                                   AWTEvent.MOUSE_MOTION_EVENT_MASK |
  760                                   AWTEvent.MOUSE_WHEEL_EVENT_MASK |
  761                                   AWTEvent.WINDOW_EVENT_MASK | sun.awt.SunToolkit.GRAB_EVENT_MASK);
  762                           return null;
  763                       }
  764                   }
  765               );
  766   
  767               Component invoker = newPath[0].getComponent();
  768               if (invoker instanceof JPopupMenu) {
  769                   invoker = ((JPopupMenu)invoker).getInvoker();
  770               }
  771               grabbedWindow = invoker instanceof Window?
  772                       (Window)invoker :
  773                       SwingUtilities.getWindowAncestor(invoker);
  774               if(grabbedWindow != null) {
  775                   if(tk instanceof sun.awt.SunToolkit) {
  776                       ((sun.awt.SunToolkit)tk).grab(grabbedWindow);
  777                   } else {
  778                       grabbedWindow.addComponentListener(this);
  779                       grabbedWindow.addWindowListener(this);
  780                   }
  781               }
  782           }
  783   
  784           void ungrabWindow() {
  785               final Toolkit tk = Toolkit.getDefaultToolkit();
  786               // The grab should be removed
  787                java.security.AccessController.doPrivileged(
  788                   new java.security.PrivilegedAction() {
  789                       public Object run() {
  790                           tk.removeAWTEventListener(MouseGrabber.this);
  791                           return null;
  792                       }
  793                   }
  794               );
  795               realUngrabWindow();
  796           }
  797   
  798           void realUngrabWindow() {
  799               Toolkit tk = Toolkit.getDefaultToolkit();
  800               if(grabbedWindow != null) {
  801                   if(tk instanceof sun.awt.SunToolkit) {
  802                       ((sun.awt.SunToolkit)tk).ungrab(grabbedWindow);
  803                   } else {
  804                       grabbedWindow.removeComponentListener(this);
  805                       grabbedWindow.removeWindowListener(this);
  806                   }
  807                   grabbedWindow = null;
  808               }
  809           }
  810   
  811           public void stateChanged(ChangeEvent e) {
  812               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  813               MenuElement[] p = msm.getSelectedPath();
  814   
  815               if (lastPathSelected.length == 0 && p.length != 0) {
  816                   grabWindow(p);
  817               }
  818   
  819               if (lastPathSelected.length != 0 && p.length == 0) {
  820                   ungrabWindow();
  821               }
  822   
  823               lastPathSelected = p;
  824           }
  825   
  826           public void eventDispatched(AWTEvent ev) {
  827               if(ev instanceof sun.awt.UngrabEvent) {
  828                   // Popup should be canceled in case of ungrab event
  829                   cancelPopupMenu( );
  830                   return;
  831               }
  832               if (!(ev instanceof MouseEvent)) {
  833                   // We are interested in MouseEvents only
  834                   return;
  835               }
  836               MouseEvent me = (MouseEvent) ev;
  837               Component src = me.getComponent();
  838               switch (me.getID()) {
  839               case MouseEvent.MOUSE_PRESSED:
  840                   if (isInPopup(src) ||
  841                       (src instanceof JMenu && ((JMenu)src).isSelected())) {
  842                       return;
  843                   }
  844                   if (!(src instanceof JComponent) ||
  845                      ! (((JComponent)src).getClientProperty("doNotCancelPopup")
  846                            == BasicComboBoxUI.HIDE_POPUP_KEY)) {
  847                       // Cancel popup only if this property was not set.
  848                       // If this property is set to TRUE component wants
  849                       // to deal with this event by himself.
  850                       cancelPopupMenu();
  851                       // Ask UIManager about should we consume event that closes
  852                       // popup. This made to match native apps behaviour.
  853                       boolean consumeEvent =
  854                           UIManager.getBoolean("PopupMenu.consumeEventOnClose");
  855                       // Consume the event so that normal processing stops.
  856                       if(consumeEvent && !(src instanceof MenuElement)) {
  857                           me.consume();
  858                       }
  859                   }
  860                   break;
  861   
  862               case MouseEvent.MOUSE_RELEASED:
  863                   if(!(src instanceof MenuElement)) {
  864                       // Do not forward event to MSM, let component handle it
  865                       if (isInPopup(src)) {
  866                           break;
  867                       }
  868                   }
  869                   if(src instanceof JMenu || !(src instanceof JMenuItem)) {
  870                       MenuSelectionManager.defaultManager().
  871                           processMouseEvent(me);
  872                   }
  873                   break;
  874               case MouseEvent.MOUSE_DRAGGED:
  875                   if(!(src instanceof MenuElement)) {
  876                       // For the MOUSE_DRAGGED event the src is
  877                       // the Component in which mouse button was pressed.
  878                       // If the src is in popupMenu,
  879                       // do not forward event to MSM, let component handle it.
  880                       if (isInPopup(src)) {
  881                           break;
  882                       }
  883                   }
  884                   MenuSelectionManager.defaultManager().
  885                       processMouseEvent(me);
  886                   break;
  887               case MouseEvent.MOUSE_WHEEL:
  888                   if (isInPopup(src)) {
  889                       return;
  890                   }
  891                   cancelPopupMenu();
  892                   break;
  893               }
  894           }
  895   
  896           boolean isInPopup(Component src) {
  897               for (Component c=src; c!=null; c=c.getParent()) {
  898                   if (c instanceof Applet || c instanceof Window) {
  899                       break;
  900                   } else if (c instanceof JPopupMenu) {
  901                       return true;
  902                   }
  903               }
  904               return false;
  905           }
  906   
  907           void cancelPopupMenu() {
  908               // We should ungrab window if a user code throws
  909               // an unexpected runtime exception. See 6495920.
  910               try {
  911                   // 4234793: This action should call firePopupMenuCanceled but it's
  912                   // a protected method. The real solution could be to make
  913                   // firePopupMenuCanceled public and call it directly.
  914                   List popups = getPopups();
  915                   Iterator iter = popups.iterator();
  916                   while (iter.hasNext()) {
  917                       JPopupMenu popup = (JPopupMenu) iter.next();
  918                       popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
  919                   }
  920                   MenuSelectionManager.defaultManager().clearSelectedPath();
  921               } catch (RuntimeException ex) {
  922                   realUngrabWindow();
  923                   throw ex;
  924               } catch (Error err) {
  925                   realUngrabWindow();
  926                   throw err;
  927               }
  928           }
  929   
  930           public void componentResized(ComponentEvent e) {
  931               cancelPopupMenu();
  932           }
  933           public void componentMoved(ComponentEvent e) {
  934               cancelPopupMenu();
  935           }
  936           public void componentShown(ComponentEvent e) {
  937               cancelPopupMenu();
  938           }
  939           public void componentHidden(ComponentEvent e) {
  940               cancelPopupMenu();
  941           }
  942           public void windowClosing(WindowEvent e) {
  943               cancelPopupMenu();
  944           }
  945           public void windowClosed(WindowEvent e) {
  946               cancelPopupMenu();
  947           }
  948           public void windowIconified(WindowEvent e) {
  949               cancelPopupMenu();
  950           }
  951           public void windowDeactivated(WindowEvent e) {
  952               cancelPopupMenu();
  953           }
  954           public void windowOpened(WindowEvent e) {}
  955           public void windowDeiconified(WindowEvent e) {}
  956           public void windowActivated(WindowEvent e) {}
  957       }
  958   
  959       /**
  960        * This helper is added to MenuSelectionManager as a ChangeListener to
  961        * listen to menu selection changes. When a menu is activated, it passes
  962        * focus to its parent JRootPane, and installs an ActionMap/InputMap pair
  963        * on that JRootPane. Those maps are necessary in order for menu
  964        * navigation to work. When menu is being deactivated, it restores focus
  965        * to the component that has had it before menu activation, and uninstalls
  966        * the maps.
  967        * This helper is also installed as a KeyListener on root pane when menu
  968        * is active. It forwards key events to MenuSelectionManager for mnemonic
  969        * keys handling.
  970        */
  971       static class MenuKeyboardHelper
  972           implements ChangeListener, KeyListener {
  973   
  974           private Component lastFocused = null;
  975           private MenuElement[] lastPathSelected = new MenuElement[0];
  976           private JPopupMenu lastPopup;
  977   
  978           private JRootPane invokerRootPane;
  979           private ActionMap menuActionMap = getActionMap();
  980           private InputMap menuInputMap;
  981           private boolean focusTraversalKeysEnabled;
  982   
  983           /*
  984            * Fix for 4213634
  985            * If this is false, KEY_TYPED and KEY_RELEASED events are NOT
  986            * processed. This is needed to avoid activating a menuitem when
  987            * the menu and menuitem share the same mnemonic.
  988            */
  989           private boolean receivedKeyPressed = false;
  990   
  991           void removeItems() {
  992               if (lastFocused != null) {
  993                   if(!lastFocused.requestFocusInWindow()) {
  994                       // Workarounr for 4810575.
  995                       // If lastFocused is not in currently focused window
  996                       // requestFocusInWindow will fail. In this case we must
  997                       // request focus by requestFocus() if it was not
  998                       // transferred from our popup.
  999                       Window cfw = KeyboardFocusManager
 1000                                    .getCurrentKeyboardFocusManager()
 1001                                     .getFocusedWindow();
 1002                       if(cfw != null &&
 1003                          "###focusableSwingPopup###".equals(cfw.getName())) {
 1004                           lastFocused.requestFocus();
 1005                       }
 1006   
 1007                   }
 1008                   lastFocused = null;
 1009               }
 1010               if (invokerRootPane != null) {
 1011                   invokerRootPane.removeKeyListener(this);
 1012                   invokerRootPane.setFocusTraversalKeysEnabled(focusTraversalKeysEnabled);
 1013                   removeUIInputMap(invokerRootPane, menuInputMap);
 1014                   removeUIActionMap(invokerRootPane, menuActionMap);
 1015                   invokerRootPane = null;
 1016               }
 1017               receivedKeyPressed = false;
 1018           }
 1019   
 1020           private FocusListener rootPaneFocusListener = new FocusAdapter() {
 1021                   public void focusGained(FocusEvent ev) {
 1022                       Component opposite = ev.getOppositeComponent();
 1023                       if (opposite != null) {
 1024                           lastFocused = opposite;
 1025                       }
 1026                       ev.getComponent().removeFocusListener(this);
 1027                   }
 1028               };
 1029   
 1030           /**
 1031            * Return the last JPopupMenu in <code>path</code>,
 1032            * or <code>null</code> if none found
 1033            */
 1034           JPopupMenu getActivePopup(MenuElement[] path) {
 1035               for (int i=path.length-1; i>=0; i--) {
 1036                   MenuElement elem = path[i];
 1037                   if (elem instanceof JPopupMenu) {
 1038                       return (JPopupMenu)elem;
 1039                   }
 1040               }
 1041               return null;
 1042           }
 1043   
 1044           void addUIInputMap(JComponent c, InputMap map) {
 1045               InputMap lastNonUI = null;
 1046               InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 1047   
 1048               while (parent != null && !(parent instanceof UIResource)) {
 1049                   lastNonUI = parent;
 1050                   parent = parent.getParent();
 1051               }
 1052   
 1053               if (lastNonUI == null) {
 1054                   c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map);
 1055               } else {
 1056                   lastNonUI.setParent(map);
 1057               }
 1058               map.setParent(parent);
 1059           }
 1060   
 1061           void addUIActionMap(JComponent c, ActionMap map) {
 1062               ActionMap lastNonUI = null;
 1063               ActionMap parent = c.getActionMap();
 1064   
 1065               while (parent != null && !(parent instanceof UIResource)) {
 1066                   lastNonUI = parent;
 1067                   parent = parent.getParent();
 1068               }
 1069   
 1070               if (lastNonUI == null) {
 1071                   c.setActionMap(map);
 1072               } else {
 1073                   lastNonUI.setParent(map);
 1074               }
 1075               map.setParent(parent);
 1076           }
 1077   
 1078           void removeUIInputMap(JComponent c, InputMap map) {
 1079               InputMap im = null;
 1080               InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 1081   
 1082               while (parent != null) {
 1083                   if (parent == map) {
 1084                       if (im == null) {
 1085                           c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,
 1086                                         map.getParent());
 1087                       } else {
 1088                           im.setParent(map.getParent());
 1089                       }
 1090                       break;
 1091                   }
 1092                   im = parent;
 1093                   parent = parent.getParent();
 1094               }
 1095           }
 1096   
 1097           void removeUIActionMap(JComponent c, ActionMap map) {
 1098               ActionMap im = null;
 1099               ActionMap parent = c.getActionMap();
 1100   
 1101               while (parent != null) {
 1102                   if (parent == map) {
 1103                       if (im == null) {
 1104                           c.setActionMap(map.getParent());
 1105                       } else {
 1106                           im.setParent(map.getParent());
 1107                       }
 1108                       break;
 1109                   }
 1110                   im = parent;
 1111                   parent = parent.getParent();
 1112               }
 1113           }
 1114   
 1115           public void stateChanged(ChangeEvent ev) {
 1116               if (!(UIManager.getLookAndFeel() instanceof BasicLookAndFeel)) {
 1117                   uninstall();
 1118                   return;
 1119               }
 1120               MenuSelectionManager msm = (MenuSelectionManager)ev.getSource();
 1121               MenuElement[] p = msm.getSelectedPath();
 1122               JPopupMenu popup = getActivePopup(p);
 1123               if (popup != null && !popup.isFocusable()) {
 1124                   // Do nothing for non-focusable popups
 1125                   return;
 1126               }
 1127   
 1128               if (lastPathSelected.length != 0 && p.length != 0 ) {
 1129                   if (!checkInvokerEqual(p[0],lastPathSelected[0])) {
 1130                       removeItems();
 1131                       lastPathSelected = new MenuElement[0];
 1132                   }
 1133               }
 1134   
 1135               if (lastPathSelected.length == 0 && p.length > 0) {
 1136                   // menu posted
 1137                   JComponent invoker;
 1138   
 1139                   if (popup == null) {
 1140                       if (p.length == 2 && p[0] instanceof JMenuBar &&
 1141                           p[1] instanceof JMenu) {
 1142                           // a menu has been selected but not open
 1143                           invoker = (JComponent)p[1];
 1144                           popup = ((JMenu)invoker).getPopupMenu();
 1145                       } else {
 1146                           return;
 1147                       }
 1148                   } else {
 1149                       Component c = popup.getInvoker();
 1150                       if(c instanceof JFrame) {
 1151                           invoker = ((JFrame)c).getRootPane();
 1152                       } else if(c instanceof JDialog) {
 1153                           invoker = ((JDialog)c).getRootPane();
 1154                       } else if(c instanceof JApplet) {
 1155                           invoker = ((JApplet)c).getRootPane();
 1156                       } else {
 1157                           while (!(c instanceof JComponent)) {
 1158                               if (c == null) {
 1159                                   return;
 1160                               }
 1161                               c = c.getParent();
 1162                           }
 1163                           invoker = (JComponent)c;
 1164                       }
 1165                   }
 1166   
 1167                   // remember current focus owner
 1168                   lastFocused = KeyboardFocusManager.
 1169                       getCurrentKeyboardFocusManager().getFocusOwner();
 1170   
 1171                   // request focus on root pane and install keybindings
 1172                   // used for menu navigation
 1173                   invokerRootPane = SwingUtilities.getRootPane(invoker);
 1174                   if (invokerRootPane != null) {
 1175                       invokerRootPane.addFocusListener(rootPaneFocusListener);
 1176                       invokerRootPane.requestFocus(true);
 1177                       invokerRootPane.addKeyListener(this);
 1178                       focusTraversalKeysEnabled = invokerRootPane.
 1179                                         getFocusTraversalKeysEnabled();
 1180                       invokerRootPane.setFocusTraversalKeysEnabled(false);
 1181   
 1182                       menuInputMap = getInputMap(popup, invokerRootPane);
 1183                       addUIInputMap(invokerRootPane, menuInputMap);
 1184                       addUIActionMap(invokerRootPane, menuActionMap);
 1185                   }
 1186               } else if (lastPathSelected.length != 0 && p.length == 0) {
 1187                   // menu hidden -- return focus to where it had been before
 1188                   // and uninstall menu keybindings
 1189                      removeItems();
 1190               } else {
 1191                   if (popup != lastPopup) {
 1192                       receivedKeyPressed = false;
 1193                   }
 1194               }
 1195   
 1196               // Remember the last path selected
 1197               lastPathSelected = p;
 1198               lastPopup = popup;
 1199           }
 1200   
 1201           public void keyPressed(KeyEvent ev) {
 1202               receivedKeyPressed = true;
 1203               MenuSelectionManager.defaultManager().processKeyEvent(ev);
 1204           }
 1205   
 1206           public void keyReleased(KeyEvent ev) {
 1207               if (receivedKeyPressed) {
 1208                   receivedKeyPressed = false;
 1209                   MenuSelectionManager.defaultManager().processKeyEvent(ev);
 1210               }
 1211           }
 1212   
 1213           public void keyTyped(KeyEvent ev) {
 1214               if (receivedKeyPressed) {
 1215                   MenuSelectionManager.defaultManager().processKeyEvent(ev);
 1216               }
 1217           }
 1218   
 1219           void uninstall() {
 1220               synchronized (MENU_KEYBOARD_HELPER_KEY) {
 1221                   MenuSelectionManager.defaultManager().removeChangeListener(this);
 1222                   AppContext.getAppContext().remove(MENU_KEYBOARD_HELPER_KEY);
 1223               }
 1224           }
 1225       }
 1226   }

Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]