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

    1   /*
    2    * Copyright (c) 1997, 2008, Oracle and/or its affiliates. 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.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * 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<JPopupMenu> getPopups() {
  229           MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  230           MenuElement[] p = msm.getSelectedPath();
  231   
  232           List<JPopupMenu> list = new ArrayList<JPopupMenu>(p.length);
  233           for (MenuElement element : p) {
  234               if (element instanceof JPopupMenu) {
  235                   list.add((JPopupMenu) element);
  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<MenuElement> lst = new ArrayList<MenuElement>(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 = 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() || item.isSelected()) {
  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;
  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           }
  376   
  377           public void menuKeyReleased(MenuKeyEvent e) {
  378           }
  379   
  380           private char lower(char keyChar) {
  381               return Character.toLowerCase(keyChar);
  382           }
  383   
  384           private char lower(int mnemonic) {
  385               return Character.toLowerCase((char) mnemonic);
  386           }
  387       }
  388   
  389       private static class Actions extends UIAction {
  390           // Types of actions
  391           private static final String CANCEL = "cancel";
  392           private static final String SELECT_NEXT = "selectNext";
  393           private static final String SELECT_PREVIOUS = "selectPrevious";
  394           private static final String SELECT_PARENT = "selectParent";
  395           private static final String SELECT_CHILD = "selectChild";
  396           private static final String RETURN = "return";
  397   
  398           // Used for next/previous actions
  399           private static final boolean FORWARD = true;
  400           private static final boolean BACKWARD = false;
  401   
  402           // Used for parent/child actions
  403           private static final boolean PARENT = false;
  404           private static final boolean CHILD = true;
  405   
  406   
  407           Actions(String key) {
  408               super(key);
  409           }
  410   
  411           public void actionPerformed(ActionEvent e) {
  412               String key = getName();
  413               if (key == CANCEL) {
  414                   cancel();
  415               }
  416               else if (key == SELECT_NEXT) {
  417                   selectItem(FORWARD);
  418               }
  419               else if (key == SELECT_PREVIOUS) {
  420                   selectItem(BACKWARD);
  421               }
  422               else if (key == SELECT_PARENT) {
  423                   selectParentChild(PARENT);
  424               }
  425               else if (key == SELECT_CHILD) {
  426                   selectParentChild(CHILD);
  427               }
  428               else if (key == RETURN) {
  429                   doReturn();
  430               }
  431           }
  432   
  433           private void doReturn() {
  434               KeyboardFocusManager fmgr =
  435                   KeyboardFocusManager.getCurrentKeyboardFocusManager();
  436               Component focusOwner = fmgr.getFocusOwner();
  437               if(focusOwner != null && !(focusOwner instanceof JRootPane)) {
  438                   return;
  439               }
  440   
  441               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  442               MenuElement path[] = msm.getSelectedPath();
  443               MenuElement lastElement;
  444               if(path.length > 0) {
  445                   lastElement = path[path.length-1];
  446                   if(lastElement instanceof JMenu) {
  447                       MenuElement newPath[] = new MenuElement[path.length+1];
  448                       System.arraycopy(path,0,newPath,0,path.length);
  449                       newPath[path.length] = ((JMenu)lastElement).getPopupMenu();
  450                       msm.setSelectedPath(newPath);
  451                   } else if(lastElement instanceof JMenuItem) {
  452                       JMenuItem mi = (JMenuItem)lastElement;
  453   
  454                       if (mi.getUI() instanceof BasicMenuItemUI) {
  455                           ((BasicMenuItemUI)mi.getUI()).doClick(msm);
  456                       }
  457                       else {
  458                           msm.clearSelectedPath();
  459                           mi.doClick(0);
  460                       }
  461                   }
  462               }
  463           }
  464           private void selectParentChild(boolean direction) {
  465               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  466               MenuElement path[] = msm.getSelectedPath();
  467               int len = path.length;
  468   
  469               if (direction == PARENT) {
  470                   // selecting parent
  471                   int popupIndex = len-1;
  472   
  473                   if (len > 2 &&
  474                       // check if we have an open submenu. A submenu item may or
  475                       // may not be selected, so submenu popup can be either the
  476                       // last or next to the last item.
  477                       (path[popupIndex] instanceof JPopupMenu ||
  478                        path[--popupIndex] instanceof JPopupMenu) &&
  479                       !((JMenu)path[popupIndex-1]).isTopLevelMenu()) {
  480   
  481                       // we have a submenu, just close it
  482                       MenuElement newPath[] = new MenuElement[popupIndex];
  483                       System.arraycopy(path, 0, newPath, 0, popupIndex);
  484                       msm.setSelectedPath(newPath);
  485                       return;
  486                   }
  487               } else {
  488                   // selecting child
  489                   if (len > 0 && path[len-1] instanceof JMenu &&
  490                       !((JMenu)path[len-1]).isTopLevelMenu()) {
  491   
  492                       // we have a submenu, open it
  493                       JMenu menu = (JMenu)path[len-1];
  494                       JPopupMenu popup = menu.getPopupMenu();
  495                       MenuElement[] subs = popup.getSubElements();
  496                       MenuElement item = findEnabledChild(subs, -1, true);
  497                       MenuElement[] newPath;
  498   
  499                       if (item == null) {
  500                           newPath = new MenuElement[len+1];
  501                       } else {
  502                           newPath = new MenuElement[len+2];
  503                           newPath[len+1] = item;
  504                       }
  505                       System.arraycopy(path, 0, newPath, 0, len);
  506                       newPath[len] = popup;
  507                       msm.setSelectedPath(newPath);
  508                       return;
  509                   }
  510               }
  511   
  512               // check if we have a toplevel menu selected.
  513               // If this is the case, we select another toplevel menu
  514               if (len > 1 && path[0] instanceof JMenuBar) {
  515                   MenuElement currentMenu = path[1];
  516                   MenuElement nextMenu = findEnabledChild(
  517                       path[0].getSubElements(), currentMenu, direction);
  518   
  519                   if (nextMenu != null && nextMenu != currentMenu) {
  520                       MenuElement newSelection[];
  521                       if (len == 2) {
  522                           // menu is selected but its popup not shown
  523                           newSelection = new MenuElement[2];
  524                           newSelection[0] = path[0];
  525                           newSelection[1] = nextMenu;
  526                       } else {
  527                           // menu is selected and its popup is shown
  528                           newSelection = new MenuElement[3];
  529                           newSelection[0] = path[0];
  530                           newSelection[1] = nextMenu;
  531                           newSelection[2] = ((JMenu)nextMenu).getPopupMenu();
  532                       }
  533                       msm.setSelectedPath(newSelection);
  534                   }
  535               }
  536           }
  537   
  538           private void selectItem(boolean direction) {
  539               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  540               MenuElement path[] = msm.getSelectedPath();
  541               if (path.length == 0) {
  542                   return;
  543               }
  544               int len = path.length;
  545               if (len == 1 && path[0] instanceof JPopupMenu) {
  546   
  547                   JPopupMenu popup = (JPopupMenu) path[0];
  548                   MenuElement[] newPath = new MenuElement[2];
  549                   newPath[0] = popup;
  550                   newPath[1] = findEnabledChild(popup.getSubElements(), -1, direction);
  551                   msm.setSelectedPath(newPath);
  552               } else if (len == 2 &&
  553                       path[0] instanceof JMenuBar && path[1] instanceof JMenu) {
  554   
  555                   // a toplevel menu is selected, but its popup not shown.
  556                   // Show the popup and select the first item
  557                   JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
  558                   MenuElement next =
  559                       findEnabledChild(popup.getSubElements(), -1, FORWARD);
  560                   MenuElement[] newPath;
  561   
  562                   if (next != null) {
  563                       // an enabled item found -- include it in newPath
  564                       newPath = new MenuElement[4];
  565                       newPath[3] = next;
  566                   } else {
  567                       // menu has no enabled items -- still must show the popup
  568                       newPath = new MenuElement[3];
  569                   }
  570                   System.arraycopy(path, 0, newPath, 0, 2);
  571                   newPath[2] = popup;
  572                   msm.setSelectedPath(newPath);
  573   
  574               } else if (path[len-1] instanceof JPopupMenu &&
  575                          path[len-2] instanceof JMenu) {
  576   
  577                   // a menu (not necessarily toplevel) is open and its popup
  578                   // shown. Select the appropriate menu item
  579                   JMenu menu = (JMenu)path[len-2];
  580                   JPopupMenu popup = menu.getPopupMenu();
  581                   MenuElement next =
  582                       findEnabledChild(popup.getSubElements(), -1, direction);
  583   
  584                   if (next != null) {
  585                       MenuElement[] newPath = new MenuElement[len+1];
  586                       System.arraycopy(path, 0, newPath, 0, len);
  587                       newPath[len] = next;
  588                       msm.setSelectedPath(newPath);
  589                   } else {
  590                       // all items in the popup are disabled.
  591                       // We're going to find the parent popup menu and select
  592                       // its next item. If there's no parent popup menu (i.e.
  593                       // current menu is toplevel), do nothing
  594                       if (len > 2 && path[len-3] instanceof JPopupMenu) {
  595                           popup = ((JPopupMenu)path[len-3]);
  596                           next = findEnabledChild(popup.getSubElements(),
  597                                                   menu, direction);
  598   
  599                           if (next != null && next != menu) {
  600                               MenuElement[] newPath = new MenuElement[len-1];
  601                               System.arraycopy(path, 0, newPath, 0, len-2);
  602                               newPath[len-2] = next;
  603                               msm.setSelectedPath(newPath);
  604                           }
  605                       }
  606                   }
  607   
  608               } else {
  609                   // just select the next item, no path expansion needed
  610                   MenuElement subs[] = path[len-2].getSubElements();
  611                   MenuElement nextChild =
  612                       findEnabledChild(subs, path[len-1], direction);
  613                   if (nextChild == null) {
  614                       nextChild = findEnabledChild(subs, -1, direction);
  615                   }
  616                   if (nextChild != null) {
  617                       path[len-1] = nextChild;
  618                       msm.setSelectedPath(path);
  619                   }
  620               }
  621           }
  622   
  623           private void cancel() {
  624               // 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's
  625               // a protected method. The real solution could be to make
  626               // firePopupMenuCanceled public and call it directly.
  627               JPopupMenu lastPopup = getLastPopup();
  628               if (lastPopup != null) {
  629                   lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
  630               }
  631               String mode = UIManager.getString("Menu.cancelMode");
  632               if ("hideMenuTree".equals(mode)) {
  633                   MenuSelectionManager.defaultManager().clearSelectedPath();
  634               } else {
  635                   shortenSelectedPath();
  636               }
  637           }
  638   
  639           private void shortenSelectedPath() {
  640               MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
  641               if (path.length <= 2) {
  642                   MenuSelectionManager.defaultManager().clearSelectedPath();
  643                   return;
  644               }
  645               // unselect MenuItem and its Popup by default
  646               int value = 2;
  647               MenuElement lastElement = path[path.length - 1];
  648               JPopupMenu lastPopup = getLastPopup();
  649               if (lastElement == lastPopup) {
  650                   MenuElement previousElement = path[path.length - 2];
  651                   if (previousElement instanceof JMenu) {
  652                       JMenu lastMenu = (JMenu) previousElement;
  653                       if (lastMenu.isEnabled() && lastPopup.getComponentCount() > 0) {
  654                           // unselect the last visible popup only
  655                           value = 1;
  656                       } else {
  657                           // unselect invisible popup and two visible elements
  658                           value = 3;
  659                       }
  660                   }
  661               }
  662               if (path.length - value <= 2
  663                       && !UIManager.getBoolean("Menu.preserveTopLevelSelection")) {
  664                   // clear selection for the topLevelMenu
  665                   value = path.length;
  666               }
  667               MenuElement newPath[] = new MenuElement[path.length - value];
  668               System.arraycopy(path, 0, newPath, 0, path.length - value);
  669               MenuSelectionManager.defaultManager().setSelectedPath(newPath);
  670           }
  671       }
  672   
  673       private static MenuElement nextEnabledChild(MenuElement e[],
  674                                                   int fromIndex, int toIndex) {
  675           for (int i=fromIndex; i<=toIndex; i++) {
  676               if (e[i] != null) {
  677                   Component comp = e[i].getComponent();
  678                   if ( comp != null
  679                           && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
  680                           && comp.isVisible()) {
  681                       return e[i];
  682                   }
  683               }
  684           }
  685           return null;
  686       }
  687   
  688       private static MenuElement previousEnabledChild(MenuElement e[],
  689                                                   int fromIndex, int toIndex) {
  690           for (int i=fromIndex; i>=toIndex; i--) {
  691               if (e[i] != null) {
  692                   Component comp = e[i].getComponent();
  693                   if ( comp != null
  694                           && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
  695                           && comp.isVisible()) {
  696                       return e[i];
  697                   }
  698               }
  699           }
  700           return null;
  701       }
  702   
  703       static MenuElement findEnabledChild(MenuElement e[], int fromIndex,
  704                                                   boolean forward) {
  705           MenuElement result;
  706           if (forward) {
  707               result = nextEnabledChild(e, fromIndex+1, e.length-1);
  708               if (result == null) result = nextEnabledChild(e, 0, fromIndex-1);
  709           } else {
  710               result = previousEnabledChild(e, fromIndex-1, 0);
  711               if (result == null) result = previousEnabledChild(e, e.length-1,
  712                                                                 fromIndex+1);
  713           }
  714           return result;
  715       }
  716   
  717       static MenuElement findEnabledChild(MenuElement e[],
  718                                      MenuElement elem, boolean forward) {
  719           for (int i=0; i<e.length; i++) {
  720               if (e[i] == elem) {
  721                   return findEnabledChild(e, i, forward);
  722               }
  723           }
  724           return null;
  725       }
  726   
  727       static class MouseGrabber implements ChangeListener,
  728           AWTEventListener, ComponentListener, WindowListener {
  729   
  730           Window grabbedWindow;
  731           MenuElement[] lastPathSelected;
  732   
  733           public MouseGrabber() {
  734               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  735               msm.addChangeListener(this);
  736               this.lastPathSelected = msm.getSelectedPath();
  737               if(this.lastPathSelected.length != 0) {
  738                   grabWindow(this.lastPathSelected);
  739               }
  740           }
  741   
  742           void uninstall() {
  743               synchronized (MOUSE_GRABBER_KEY) {
  744                   MenuSelectionManager.defaultManager().removeChangeListener(this);
  745                   ungrabWindow();
  746                   AppContext.getAppContext().remove(MOUSE_GRABBER_KEY);
  747               }
  748           }
  749   
  750           void grabWindow(MenuElement[] newPath) {
  751               // A grab needs to be added
  752               final Toolkit tk = Toolkit.getDefaultToolkit();
  753               java.security.AccessController.doPrivileged(
  754                   new java.security.PrivilegedAction<Object>() {
  755                       public Object run() {
  756                           tk.addAWTEventListener(MouseGrabber.this,
  757                                   AWTEvent.MOUSE_EVENT_MASK |
  758                                   AWTEvent.MOUSE_MOTION_EVENT_MASK |
  759                                   AWTEvent.MOUSE_WHEEL_EVENT_MASK |
  760                                   AWTEvent.WINDOW_EVENT_MASK | sun.awt.SunToolkit.GRAB_EVENT_MASK);
  761                           return null;
  762                       }
  763                   }
  764               );
  765   
  766               Component invoker = newPath[0].getComponent();
  767               if (invoker instanceof JPopupMenu) {
  768                   invoker = ((JPopupMenu)invoker).getInvoker();
  769               }
  770               grabbedWindow = invoker instanceof Window?
  771                       (Window)invoker :
  772                       SwingUtilities.getWindowAncestor(invoker);
  773               if(grabbedWindow != null) {
  774                   if(tk instanceof sun.awt.SunToolkit) {
  775                       ((sun.awt.SunToolkit)tk).grab(grabbedWindow);
  776                   } else {
  777                       grabbedWindow.addComponentListener(this);
  778                       grabbedWindow.addWindowListener(this);
  779                   }
  780               }
  781           }
  782   
  783           void ungrabWindow() {
  784               final Toolkit tk = Toolkit.getDefaultToolkit();
  785               // The grab should be removed
  786                java.security.AccessController.doPrivileged(
  787                   new java.security.PrivilegedAction<Object>() {
  788                       public Object run() {
  789                           tk.removeAWTEventListener(MouseGrabber.this);
  790                           return null;
  791                       }
  792                   }
  793               );
  794               realUngrabWindow();
  795           }
  796   
  797           void realUngrabWindow() {
  798               Toolkit tk = Toolkit.getDefaultToolkit();
  799               if(grabbedWindow != null) {
  800                   if(tk instanceof sun.awt.SunToolkit) {
  801                       ((sun.awt.SunToolkit)tk).ungrab(grabbedWindow);
  802                   } else {
  803                       grabbedWindow.removeComponentListener(this);
  804                       grabbedWindow.removeWindowListener(this);
  805                   }
  806                   grabbedWindow = null;
  807               }
  808           }
  809   
  810           public void stateChanged(ChangeEvent e) {
  811               MenuSelectionManager msm = MenuSelectionManager.defaultManager();
  812               MenuElement[] p = msm.getSelectedPath();
  813   
  814               if (lastPathSelected.length == 0 && p.length != 0) {
  815                   grabWindow(p);
  816               }
  817   
  818               if (lastPathSelected.length != 0 && p.length == 0) {
  819                   ungrabWindow();
  820               }
  821   
  822               lastPathSelected = p;
  823           }
  824   
  825           public void eventDispatched(AWTEvent ev) {
  826               if(ev instanceof sun.awt.UngrabEvent) {
  827                   // Popup should be canceled in case of ungrab event
  828                   cancelPopupMenu( );
  829                   return;
  830               }
  831               if (!(ev instanceof MouseEvent)) {
  832                   // We are interested in MouseEvents only
  833                   return;
  834               }
  835               MouseEvent me = (MouseEvent) ev;
  836               Component src = me.getComponent();
  837               switch (me.getID()) {
  838               case MouseEvent.MOUSE_PRESSED:
  839                   if (isInPopup(src) ||
  840                       (src instanceof JMenu && ((JMenu)src).isSelected())) {
  841                       return;
  842                   }
  843                   if (!(src instanceof JComponent) ||
  844                      ! (((JComponent)src).getClientProperty("doNotCancelPopup")
  845                            == BasicComboBoxUI.HIDE_POPUP_KEY)) {
  846                       // Cancel popup only if this property was not set.
  847                       // If this property is set to TRUE component wants
  848                       // to deal with this event by himself.
  849                       cancelPopupMenu();
  850                       // Ask UIManager about should we consume event that closes
  851                       // popup. This made to match native apps behaviour.
  852                       boolean consumeEvent =
  853                           UIManager.getBoolean("PopupMenu.consumeEventOnClose");
  854                       // Consume the event so that normal processing stops.
  855                       if(consumeEvent && !(src instanceof MenuElement)) {
  856                           me.consume();
  857                       }
  858                   }
  859                   break;
  860   
  861               case MouseEvent.MOUSE_RELEASED:
  862                   if(!(src instanceof MenuElement)) {
  863                       // Do not forward event to MSM, let component handle it
  864                       if (isInPopup(src)) {
  865                           break;
  866                       }
  867                   }
  868                   if(src instanceof JMenu || !(src instanceof JMenuItem)) {
  869                       MenuSelectionManager.defaultManager().
  870                           processMouseEvent(me);
  871                   }
  872                   break;
  873               case MouseEvent.MOUSE_DRAGGED:
  874                   if(!(src instanceof MenuElement)) {
  875                       // For the MOUSE_DRAGGED event the src is
  876                       // the Component in which mouse button was pressed.
  877                       // If the src is in popupMenu,
  878                       // do not forward event to MSM, let component handle it.
  879                       if (isInPopup(src)) {
  880                           break;
  881                       }
  882                   }
  883                   MenuSelectionManager.defaultManager().
  884                       processMouseEvent(me);
  885                   break;
  886               case MouseEvent.MOUSE_WHEEL:
  887                   if (isInPopup(src)) {
  888                       return;
  889                   }
  890                   cancelPopupMenu();
  891                   break;
  892               }
  893           }
  894   
  895           boolean isInPopup(Component src) {
  896               for (Component c=src; c!=null; c=c.getParent()) {
  897                   if (c instanceof Applet || c instanceof Window) {
  898                       break;
  899                   } else if (c instanceof JPopupMenu) {
  900                       return true;
  901                   }
  902               }
  903               return false;
  904           }
  905   
  906           void cancelPopupMenu() {
  907               // We should ungrab window if a user code throws
  908               // an unexpected runtime exception. See 6495920.
  909               try {
  910                   // 4234793: This action should call firePopupMenuCanceled but it's
  911                   // a protected method. The real solution could be to make
  912                   // firePopupMenuCanceled public and call it directly.
  913                   List<JPopupMenu> popups = getPopups();
  914                   for (JPopupMenu popup : popups) {
  915                       popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
  916                   }
  917                   MenuSelectionManager.defaultManager().clearSelectedPath();
  918               } catch (RuntimeException ex) {
  919                   realUngrabWindow();
  920                   throw ex;
  921               } catch (Error err) {
  922                   realUngrabWindow();
  923                   throw err;
  924               }
  925           }
  926   
  927           public void componentResized(ComponentEvent e) {
  928               cancelPopupMenu();
  929           }
  930           public void componentMoved(ComponentEvent e) {
  931               cancelPopupMenu();
  932           }
  933           public void componentShown(ComponentEvent e) {
  934               cancelPopupMenu();
  935           }
  936           public void componentHidden(ComponentEvent e) {
  937               cancelPopupMenu();
  938           }
  939           public void windowClosing(WindowEvent e) {
  940               cancelPopupMenu();
  941           }
  942           public void windowClosed(WindowEvent e) {
  943               cancelPopupMenu();
  944           }
  945           public void windowIconified(WindowEvent e) {
  946               cancelPopupMenu();
  947           }
  948           public void windowDeactivated(WindowEvent e) {
  949               cancelPopupMenu();
  950           }
  951           public void windowOpened(WindowEvent e) {}
  952           public void windowDeiconified(WindowEvent e) {}
  953           public void windowActivated(WindowEvent e) {}
  954       }
  955   
  956       /**
  957        * This helper is added to MenuSelectionManager as a ChangeListener to
  958        * listen to menu selection changes. When a menu is activated, it passes
  959        * focus to its parent JRootPane, and installs an ActionMap/InputMap pair
  960        * on that JRootPane. Those maps are necessary in order for menu
  961        * navigation to work. When menu is being deactivated, it restores focus
  962        * to the component that has had it before menu activation, and uninstalls
  963        * the maps.
  964        * This helper is also installed as a KeyListener on root pane when menu
  965        * is active. It forwards key events to MenuSelectionManager for mnemonic
  966        * keys handling.
  967        */
  968       static class MenuKeyboardHelper
  969           implements ChangeListener, KeyListener {
  970   
  971           private Component lastFocused = null;
  972           private MenuElement[] lastPathSelected = new MenuElement[0];
  973           private JPopupMenu lastPopup;
  974   
  975           private JRootPane invokerRootPane;
  976           private ActionMap menuActionMap = getActionMap();
  977           private InputMap menuInputMap;
  978           private boolean focusTraversalKeysEnabled;
  979   
  980           /*
  981            * Fix for 4213634
  982            * If this is false, KEY_TYPED and KEY_RELEASED events are NOT
  983            * processed. This is needed to avoid activating a menuitem when
  984            * the menu and menuitem share the same mnemonic.
  985            */
  986           private boolean receivedKeyPressed = false;
  987   
  988           void removeItems() {
  989               if (lastFocused != null) {
  990                   if(!lastFocused.requestFocusInWindow()) {
  991                       // Workarounr for 4810575.
  992                       // If lastFocused is not in currently focused window
  993                       // requestFocusInWindow will fail. In this case we must
  994                       // request focus by requestFocus() if it was not
  995                       // transferred from our popup.
  996                       Window cfw = KeyboardFocusManager
  997                                    .getCurrentKeyboardFocusManager()
  998                                     .getFocusedWindow();
  999                       if(cfw != null &&
 1000                          "###focusableSwingPopup###".equals(cfw.getName())) {
 1001                           lastFocused.requestFocus();
 1002                       }
 1003   
 1004                   }
 1005                   lastFocused = null;
 1006               }
 1007               if (invokerRootPane != null) {
 1008                   invokerRootPane.removeKeyListener(this);
 1009                   invokerRootPane.setFocusTraversalKeysEnabled(focusTraversalKeysEnabled);
 1010                   removeUIInputMap(invokerRootPane, menuInputMap);
 1011                   removeUIActionMap(invokerRootPane, menuActionMap);
 1012                   invokerRootPane = null;
 1013               }
 1014               receivedKeyPressed = false;
 1015           }
 1016   
 1017           private FocusListener rootPaneFocusListener = new FocusAdapter() {
 1018                   public void focusGained(FocusEvent ev) {
 1019                       Component opposite = ev.getOppositeComponent();
 1020                       if (opposite != null) {
 1021                           lastFocused = opposite;
 1022                       }
 1023                       ev.getComponent().removeFocusListener(this);
 1024                   }
 1025               };
 1026   
 1027           /**
 1028            * Return the last JPopupMenu in <code>path</code>,
 1029            * or <code>null</code> if none found
 1030            */
 1031           JPopupMenu getActivePopup(MenuElement[] path) {
 1032               for (int i=path.length-1; i>=0; i--) {
 1033                   MenuElement elem = path[i];
 1034                   if (elem instanceof JPopupMenu) {
 1035                       return (JPopupMenu)elem;
 1036                   }
 1037               }
 1038               return null;
 1039           }
 1040   
 1041           void addUIInputMap(JComponent c, InputMap map) {
 1042               InputMap lastNonUI = null;
 1043               InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 1044   
 1045               while (parent != null && !(parent instanceof UIResource)) {
 1046                   lastNonUI = parent;
 1047                   parent = parent.getParent();
 1048               }
 1049   
 1050               if (lastNonUI == null) {
 1051                   c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map);
 1052               } else {
 1053                   lastNonUI.setParent(map);
 1054               }
 1055               map.setParent(parent);
 1056           }
 1057   
 1058           void addUIActionMap(JComponent c, ActionMap map) {
 1059               ActionMap lastNonUI = null;
 1060               ActionMap parent = c.getActionMap();
 1061   
 1062               while (parent != null && !(parent instanceof UIResource)) {
 1063                   lastNonUI = parent;
 1064                   parent = parent.getParent();
 1065               }
 1066   
 1067               if (lastNonUI == null) {
 1068                   c.setActionMap(map);
 1069               } else {
 1070                   lastNonUI.setParent(map);
 1071               }
 1072               map.setParent(parent);
 1073           }
 1074   
 1075           void removeUIInputMap(JComponent c, InputMap map) {
 1076               InputMap im = null;
 1077               InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 1078   
 1079               while (parent != null) {
 1080                   if (parent == map) {
 1081                       if (im == null) {
 1082                           c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,
 1083                                         map.getParent());
 1084                       } else {
 1085                           im.setParent(map.getParent());
 1086                       }
 1087                       break;
 1088                   }
 1089                   im = parent;
 1090                   parent = parent.getParent();
 1091               }
 1092           }
 1093   
 1094           void removeUIActionMap(JComponent c, ActionMap map) {
 1095               ActionMap im = null;
 1096               ActionMap parent = c.getActionMap();
 1097   
 1098               while (parent != null) {
 1099                   if (parent == map) {
 1100                       if (im == null) {
 1101                           c.setActionMap(map.getParent());
 1102                       } else {
 1103                           im.setParent(map.getParent());
 1104                       }
 1105                       break;
 1106                   }
 1107                   im = parent;
 1108                   parent = parent.getParent();
 1109               }
 1110           }
 1111   
 1112           public void stateChanged(ChangeEvent ev) {
 1113               if (!(UIManager.getLookAndFeel() instanceof BasicLookAndFeel)) {
 1114                   uninstall();
 1115                   return;
 1116               }
 1117               MenuSelectionManager msm = (MenuSelectionManager)ev.getSource();
 1118               MenuElement[] p = msm.getSelectedPath();
 1119               JPopupMenu popup = getActivePopup(p);
 1120               if (popup != null && !popup.isFocusable()) {
 1121                   // Do nothing for non-focusable popups
 1122                   return;
 1123               }
 1124   
 1125               if (lastPathSelected.length != 0 && p.length != 0 ) {
 1126                   if (!checkInvokerEqual(p[0],lastPathSelected[0])) {
 1127                       removeItems();
 1128                       lastPathSelected = new MenuElement[0];
 1129                   }
 1130               }
 1131   
 1132               if (lastPathSelected.length == 0 && p.length > 0) {
 1133                   // menu posted
 1134                   JComponent invoker;
 1135   
 1136                   if (popup == null) {
 1137                       if (p.length == 2 && p[0] instanceof JMenuBar &&
 1138                           p[1] instanceof JMenu) {
 1139                           // a menu has been selected but not open
 1140                           invoker = (JComponent)p[1];
 1141                           popup = ((JMenu)invoker).getPopupMenu();
 1142                       } else {
 1143                           return;
 1144                       }
 1145                   } else {
 1146                       Component c = popup.getInvoker();
 1147                       if(c instanceof JFrame) {
 1148                           invoker = ((JFrame)c).getRootPane();
 1149                       } else if(c instanceof JDialog) {
 1150                           invoker = ((JDialog)c).getRootPane();
 1151                       } else if(c instanceof JApplet) {
 1152                           invoker = ((JApplet)c).getRootPane();
 1153                       } else {
 1154                           while (!(c instanceof JComponent)) {
 1155                               if (c == null) {
 1156                                   return;
 1157                               }
 1158                               c = c.getParent();
 1159                           }
 1160                           invoker = (JComponent)c;
 1161                       }
 1162                   }
 1163   
 1164                   // remember current focus owner
 1165                   lastFocused = KeyboardFocusManager.
 1166                       getCurrentKeyboardFocusManager().getFocusOwner();
 1167   
 1168                   // request focus on root pane and install keybindings
 1169                   // used for menu navigation
 1170                   invokerRootPane = SwingUtilities.getRootPane(invoker);
 1171                   if (invokerRootPane != null) {
 1172                       invokerRootPane.addFocusListener(rootPaneFocusListener);
 1173                       invokerRootPane.requestFocus(true);
 1174                       invokerRootPane.addKeyListener(this);
 1175                       focusTraversalKeysEnabled = invokerRootPane.
 1176                                         getFocusTraversalKeysEnabled();
 1177                       invokerRootPane.setFocusTraversalKeysEnabled(false);
 1178   
 1179                       menuInputMap = getInputMap(popup, invokerRootPane);
 1180                       addUIInputMap(invokerRootPane, menuInputMap);
 1181                       addUIActionMap(invokerRootPane, menuActionMap);
 1182                   }
 1183               } else if (lastPathSelected.length != 0 && p.length == 0) {
 1184                   // menu hidden -- return focus to where it had been before
 1185                   // and uninstall menu keybindings
 1186                      removeItems();
 1187               } else {
 1188                   if (popup != lastPopup) {
 1189                       receivedKeyPressed = false;
 1190                   }
 1191               }
 1192   
 1193               // Remember the last path selected
 1194               lastPathSelected = p;
 1195               lastPopup = popup;
 1196           }
 1197   
 1198           public void keyPressed(KeyEvent ev) {
 1199               receivedKeyPressed = true;
 1200               MenuSelectionManager.defaultManager().processKeyEvent(ev);
 1201           }
 1202   
 1203           public void keyReleased(KeyEvent ev) {
 1204               if (receivedKeyPressed) {
 1205                   receivedKeyPressed = false;
 1206                   MenuSelectionManager.defaultManager().processKeyEvent(ev);
 1207               }
 1208           }
 1209   
 1210           public void keyTyped(KeyEvent ev) {
 1211               if (receivedKeyPressed) {
 1212                   MenuSelectionManager.defaultManager().processKeyEvent(ev);
 1213               }
 1214           }
 1215   
 1216           void uninstall() {
 1217               synchronized (MENU_KEYBOARD_HELPER_KEY) {
 1218                   MenuSelectionManager.defaultManager().removeChangeListener(this);
 1219                   AppContext.getAppContext().remove(MENU_KEYBOARD_HELPER_KEY);
 1220               }
 1221           }
 1222       }
 1223   }

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