KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicPopupMenuUI


1 /*
2  * @(#)BasicPopupMenuUI.java 1.123 06/12/20
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.plaf.basic;
9
10 import javax.swing.*;
11 import javax.swing.event.*;
12 import javax.swing.plaf.*;
13 import javax.swing.plaf.basic.*;
14 import javax.swing.border.*;
15
16 import java.applet.Applet JavaDoc;
17
18 import java.awt.Component JavaDoc;
19 import java.awt.Container JavaDoc;
20 import java.awt.Dimension JavaDoc;
21 import java.awt.KeyboardFocusManager JavaDoc;
22 import java.awt.Window JavaDoc;
23 import java.awt.event.*;
24 import java.awt.AWTEvent JavaDoc;
25 import java.awt.Toolkit JavaDoc;
26
27 import java.beans.PropertyChangeListener JavaDoc;
28 import java.beans.PropertyChangeEvent JavaDoc;
29
30 import java.util.*;
31
32 import sun.swing.DefaultLookup;
33 import sun.swing.UIAction;
34
35 /**
36  * A Windows L&F implementation of PopupMenuUI. This implementation
37  * is a "combined" view/controller.
38  *
39  * @version 1.123 12/20/06
40  * @author Georges Saab
41  * @author David Karlton
42  * @author Arnaud Weber
43  */

44 public class BasicPopupMenuUI extends PopupMenuUI {
45     protected JPopupMenu popupMenu = null;
46     private transient PopupMenuListener popupMenuListener = null;
47     private MenuKeyListener menuKeyListener = null;
48     static boolean menuKeyboardHelperInstalled = false;
49     static MenuKeyboardHelper menuKeyboardHelper = null;
50
51     private static boolean checkedUnpostPopup;
52     private static boolean unpostPopup;
53
54     public static ComponentUI createUI(JComponent x) {
55     return new BasicPopupMenuUI JavaDoc();
56     }
57
58     public BasicPopupMenuUI() {
59         BasicLookAndFeel.hasPopups = true;
60         LookAndFeel laf = UIManager.getLookAndFeel();
61         if (laf instanceof BasicLookAndFeel JavaDoc) {
62             ((BasicLookAndFeel JavaDoc)laf).createdPopup();
63         }
64     }
65
66     public void installUI(JComponent c) {
67     popupMenu = (JPopupMenu) c;
68
69     installDefaults();
70         installListeners();
71         installKeyboardActions();
72     }
73
74     public void installDefaults() {
75     if (popupMenu.getLayout() == null ||
76         popupMenu.getLayout() instanceof UIResource)
77         popupMenu.setLayout(new DefaultMenuLayout JavaDoc(popupMenu, BoxLayout.Y_AXIS));
78
79     LookAndFeel.installProperty(popupMenu, "opaque", Boolean.TRUE);
80     LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
81     LookAndFeel.installColorsAndFont(popupMenu,
82                      "PopupMenu.background",
83                      "PopupMenu.foreground",
84                      "PopupMenu.font");
85     }
86     
87     protected void installListeners() {
88         if (popupMenuListener == null) {
89         popupMenuListener = new BasicPopupMenuListener();
90     }
91     popupMenu.addPopupMenuListener(popupMenuListener);
92
93         if (menuKeyListener == null) {
94             menuKeyListener = new BasicMenuKeyListener();
95         }
96         popupMenu.addMenuKeyListener(menuKeyListener);
97
98     if (mouseGrabber == null) {
99         mouseGrabber = new MouseGrabber();
100     }
101
102         if (!menuKeyboardHelperInstalled) {
103             if (menuKeyboardHelper == null) {
104                 menuKeyboardHelper = new MenuKeyboardHelper();
105             }
106             MenuSelectionManager msm = MenuSelectionManager.defaultManager();
107             msm.addChangeListener(menuKeyboardHelper);
108             menuKeyboardHelperInstalled = true;
109         }
110     }
111
112     protected void installKeyboardActions() {
113     }
114
115     static InputMap getInputMap(JPopupMenu popup, JComponent c) {
116         InputMap windowInputMap = null;
117     Object JavaDoc[] bindings = (Object JavaDoc[])UIManager.get("PopupMenu.selectedWindowInputMapBindings");
118     if (bindings != null) {
119         windowInputMap = LookAndFeel.makeComponentInputMap(c, bindings);
120         if (!popup.getComponentOrientation().isLeftToRight()) {
121         Object JavaDoc[] km = (Object JavaDoc[])UIManager.get("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
122         if (km != null) {
123             InputMap rightToLeftInputMap = LookAndFeel.makeComponentInputMap(c, km);
124             rightToLeftInputMap.setParent(windowInputMap);
125             windowInputMap = rightToLeftInputMap;
126         }
127         }
128         }
129         return windowInputMap;
130     }
131
132     static ActionMap getActionMap() {
133         return LazyActionMap.getActionMap(BasicPopupMenuUI JavaDoc.class,
134                                           "PopupMenu.actionMap");
135     }
136   
137     static void loadActionMap(LazyActionMap JavaDoc map) {
138     map.put(new Actions(Actions.CANCEL));
139         map.put(new Actions(Actions.SELECT_NEXT));
140         map.put(new Actions(Actions.SELECT_PREVIOUS));
141     map.put(new Actions(Actions.SELECT_PARENT));
142     map.put(new Actions(Actions.SELECT_CHILD));
143     map.put(new Actions(Actions.RETURN));
144         BasicLookAndFeel.installAudioActionMap(map);
145     }
146
147     public void uninstallUI(JComponent c) {
148         uninstallDefaults();
149         uninstallListeners();
150         uninstallKeyboardActions();
151     
152     popupMenu = null;
153     }
154     
155     protected void uninstallDefaults() {
156     LookAndFeel.uninstallBorder(popupMenu);
157     }
158
159     protected void uninstallListeners() {
160         if (popupMenuListener != null) {
161             popupMenu.removePopupMenuListener(popupMenuListener);
162     }
163         if (menuKeyListener != null) {
164             popupMenu.removeMenuKeyListener(menuKeyListener);
165         }
166         if(mouseGrabber != null) {
167             MenuSelectionManager msm = MenuSelectionManager.defaultManager();
168             msm.removeChangeListener(mouseGrabber);
169             mouseGrabber.ungrabWindow();
170             mouseGrabber = null;
171         }
172     }
173
174     protected void uninstallKeyboardActions() {
175     SwingUtilities.replaceUIActionMap(popupMenu, null);
176     SwingUtilities.replaceUIInputMap(popupMenu,
177                   JComponent.WHEN_IN_FOCUSED_WINDOW, null);
178     }
179
180     static MenuElement getFirstPopup() {
181     MenuSelectionManager msm = MenuSelectionManager.defaultManager();
182     MenuElement[] p = msm.getSelectedPath();
183     MenuElement me = null;
184     
185     for(int i = 0 ; me == null && i < p.length ; i++) {
186         if (p[i] instanceof JPopupMenu)
187         me = p[i];
188     }
189     
190     return me;
191     }
192
193     private static boolean doUnpostPopupOnDeactivation() {
194         if (!checkedUnpostPopup) {
195             Boolean JavaDoc b = java.security.AccessController.doPrivileged(
196                 new java.security.PrivilegedAction JavaDoc <Boolean JavaDoc> () {
197                     public Boolean JavaDoc run() {
198                         String JavaDoc pKey =
199                             "sun.swing.unpostPopupsOnWindowDeactivation";
200                         String JavaDoc value = System.getProperty(pKey, "true");
201                         return Boolean.valueOf(value);
202                     }
203                 }
204             );
205             unpostPopup = b.booleanValue();
206             checkedUnpostPopup = true;
207         }
208         return unpostPopup;
209     }
210
211     static JPopupMenu getLastPopup() {
212     MenuSelectionManager msm = MenuSelectionManager.defaultManager();
213     MenuElement[] p = msm.getSelectedPath();
214     JPopupMenu popup = null;
215     
216     for(int i = p.length - 1; popup == null && i >= 0; i--) {
217         if (p[i] instanceof JPopupMenu)
218         popup = (JPopupMenu)p[i];
219     }
220     return popup;
221     }
222
223     static List getPopups() {
224     MenuSelectionManager msm = MenuSelectionManager.defaultManager();
225     MenuElement[] p = msm.getSelectedPath();
226     
227     List list = new ArrayList(p.length);
228     for(int i = 0; i < p.length; i++) {
229         if (p[i] instanceof JPopupMenu) {
230         list.add((JPopupMenu)p[i]);
231         }
232     }
233     return list;
234     }
235
236     public boolean isPopupTrigger(MouseEvent e) {
237     return ((e.getID()==MouseEvent.MOUSE_RELEASED)
238         && ((e.getModifiers() & MouseEvent.BUTTON3_MASK)!=0));
239     }
240
241     private static boolean checkInvokerEqual(MenuElement present, MenuElement last) {
242         Component JavaDoc invokerPresent = present.getComponent();
243         Component JavaDoc invokerLast = last.getComponent();
244
245         if (invokerPresent instanceof JPopupMenu) {
246             invokerPresent = ((JPopupMenu)invokerPresent).getInvoker();
247     }
248         if (invokerLast instanceof JPopupMenu) {
249             invokerLast = ((JPopupMenu)invokerLast).getInvoker();
250         }
251         return (invokerPresent == invokerLast);
252     }
253
254
255     /**
256      * This Listener fires the Action that provides the correct auditory
257      * feedback.
258      *
259      * @since 1.4
260      */

261     private class BasicPopupMenuListener implements PopupMenuListener {
262         public void popupMenuCanceled(PopupMenuEvent e) {
263     }
264
265         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
266     }
267
268         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
269             BasicLookAndFeel.playSound((JPopupMenu)e.getSource(),
270                                        "PopupMenu.popupSound");
271     }
272     }
273
274     /**
275      * Handles mnemonic for children JMenuItems.
276      * @since 1.5
277      */

278     private class BasicMenuKeyListener implements MenuKeyListener {
279         MenuElement menuToOpen = null;
280
281     public void menuKeyTyped(MenuKeyEvent e) {
282             if (menuToOpen != null) {
283                 // we have a submenu to open
284
JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu();
285                 MenuElement subitem = findEnabledChild(
286                         subpopup.getSubElements(), -1, true);
287
288                 ArrayList lst = new ArrayList(Arrays.asList(e.getPath()));
289                 lst.add(menuToOpen);
290                 lst.add(subpopup);
291                 if (subitem != null) {
292                     lst.add(subitem);
293                 }
294                 MenuElement newPath[] = new MenuElement[0];;
295                 newPath = (MenuElement[])lst.toArray(newPath);
296                 MenuSelectionManager.defaultManager().setSelectedPath(newPath);
297                 e.consume();
298             }
299             menuToOpen = null;
300         }
301
302     public void menuKeyPressed(MenuKeyEvent e) {
303         // Handle the case for Escape or Enter...
304
if (!Character.isLetterOrDigit(e.getKeyChar())) {
305         return;
306             }
307
308             int keyCode = e.getKeyCode();
309             MenuSelectionManager manager = e.getMenuSelectionManager();
310             MenuElement path[] = e.getPath();
311             MenuElement items[] = popupMenu.getSubElements();
312             int currentIndex = -1;
313             int matches = 0;
314             int firstMatch = -1;
315             int indexes[] = null;
316
317             for (int j = 0; j < items.length; j++) {
318                 if (! (items[j] instanceof JMenuItem)) {
319                     continue;
320                 }
321                 JMenuItem item = (JMenuItem)items[j];
322                 if (item.isEnabled() &&
323                     item.isVisible() && keyCode == item.getMnemonic()) {
324                     if (matches == 0) {
325                         firstMatch = j;
326                         matches++;
327                     } else {
328                         if (indexes == null) {
329                             indexes = new int[items.length];
330                             indexes[0] = firstMatch;
331                         }
332                         indexes[matches++] = j;
333                     }
334                 }
335                 if (item.isArmed()) {
336                     currentIndex = matches - 1;
337                 }
338             }
339
340             if (matches == 0) {
341                 ; // no op
342
} else if (matches == 1) {
343                 // Invoke the menu action
344
JMenuItem item = (JMenuItem)items[firstMatch];
345                 if (item instanceof JMenu) {
346                     // submenus are handled in menuKeyTyped
347
menuToOpen = item;
348                 } else if (item.isEnabled()) {
349                     // we have a menu item
350
manager.clearSelectedPath();
351                     item.doClick();
352                 }
353                 e.consume();
354             } else {
355                 // Select the menu item with the matching mnemonic. If
356
// the same mnemonic has been invoked then select the next
357
// menu item in the cycle.
358
MenuElement newItem = null;
359
360                 newItem = items[indexes[(currentIndex + 1) % matches]];
361
362                 MenuElement newPath[] = new MenuElement[path.length+1];
363                 System.arraycopy(path, 0, newPath, 0, path.length);
364                 newPath[path.length] = newItem;
365                 manager.setSelectedPath(newPath);
366                 e.consume();
367             }
368             return;
369     }
370
371     public void menuKeyReleased(MenuKeyEvent e) {
372         }
373     }
374
375     private static class Actions extends UIAction {
376         // Types of actions
377
private static final String JavaDoc CANCEL = "cancel";
378         private static final String JavaDoc SELECT_NEXT = "selectNext";
379         private static final String JavaDoc SELECT_PREVIOUS = "selectPrevious";
380         private static final String JavaDoc SELECT_PARENT = "selectParent";
381         private static final String JavaDoc SELECT_CHILD = "selectChild";
382         private static final String JavaDoc RETURN = "return";
383
384         // Used for next/previous actions
385
private static final boolean FORWARD = true;
386         private static final boolean BACKWARD = false;
387
388         // Used for parent/child actions
389
private static final boolean PARENT = false;
390         private static final boolean CHILD = true;
391
392
393         Actions(String JavaDoc key) {
394             super(key);
395         }
396
397     public void actionPerformed(ActionEvent e) {
398             String JavaDoc key = getName();
399             if (key == CANCEL) {
400                 cancel();
401             }
402             else if (key == SELECT_NEXT) {
403                 selectItem(FORWARD);
404             }
405             else if (key == SELECT_PREVIOUS) {
406                 selectItem(BACKWARD);
407             }
408             else if (key == SELECT_PARENT) {
409                 selectParentChild(PARENT);
410             }
411             else if (key == SELECT_CHILD) {
412                 selectParentChild(CHILD);
413             }
414             else if (key == RETURN) {
415                 doReturn();
416             }
417         }
418
419     private void doReturn() {
420             KeyboardFocusManager JavaDoc fmgr =
421                 KeyboardFocusManager.getCurrentKeyboardFocusManager();
422             Component JavaDoc focusOwner = fmgr.getFocusOwner();
423             if(focusOwner != null && !(focusOwner instanceof JRootPane)) {
424                 return;
425             }
426
427             MenuSelectionManager msm = MenuSelectionManager.defaultManager();
428         MenuElement path[] = msm.getSelectedPath();
429         MenuElement lastElement;
430         if(path.length > 0) {
431         lastElement = path[path.length-1];
432         if(lastElement instanceof JMenu) {
433             MenuElement newPath[] = new MenuElement[path.length+1];
434             System.arraycopy(path,0,newPath,0,path.length);
435             newPath[path.length] = ((JMenu)lastElement).getPopupMenu();
436             msm.setSelectedPath(newPath);
437         } else if(lastElement instanceof JMenuItem) {
438                     JMenuItem mi = (JMenuItem)lastElement;
439
440                     if (mi.getUI() instanceof BasicMenuItemUI JavaDoc) {
441                         ((BasicMenuItemUI JavaDoc)mi.getUI()).doClick(msm);
442                     }
443                     else {
444                         msm.clearSelectedPath();
445                         mi.doClick(0);
446                     }
447         }
448         }
449     }
450     private void selectParentChild(boolean direction) {
451             MenuSelectionManager msm = MenuSelectionManager.defaultManager();
452             MenuElement path[] = msm.getSelectedPath();
453             int len = path.length;
454
455             if (direction == PARENT) {
456                 // selecting parent
457
int popupIndex = len-1;
458
459                 if (len > 2 &&
460                     // check if we have an open submenu. A submenu item may or
461
// may not be selected, so submenu popup can be either the
462
// last or next to the last item.
463
(path[popupIndex] instanceof JPopupMenu ||
464                      path[--popupIndex] instanceof JPopupMenu) &&
465                     !((JMenu)path[popupIndex-1]).isTopLevelMenu()) {
466
467                     // we have a submenu, just close it
468
MenuElement newPath[] = new MenuElement[popupIndex];
469                     System.arraycopy(path, 0, newPath, 0, popupIndex);
470                     msm.setSelectedPath(newPath);
471                     return;
472                 }
473             } else {
474                 // selecting child
475
if (len > 0 && path[len-1] instanceof JMenu &&
476                     !((JMenu)path[len-1]).isTopLevelMenu()) {
477
478                     // we have a submenu, open it
479
JMenu menu = (JMenu)path[len-1];
480                     JPopupMenu popup = menu.getPopupMenu();
481                     MenuElement[] subs = popup.getSubElements();
482                     MenuElement item = findEnabledChild(subs, -1, true);
483                     MenuElement[] newPath;
484
485                     if (item == null) {
486                         newPath = new MenuElement[len+1];
487                     } else {
488                         newPath = new MenuElement[len+2];
489                         newPath[len+1] = item;
490                     }
491                     System.arraycopy(path, 0, newPath, 0, len);
492                     newPath[len] = popup;
493                     msm.setSelectedPath(newPath);
494                     return;
495                 }
496             }
497
498             // check if we have a toplevel menu selected.
499
// If this is the case, we select another toplevel menu
500
if (len > 1 && path[0] instanceof JMenuBar) {
501                 MenuElement currentMenu = path[1];
502         MenuElement nextMenu = findEnabledChild(
503                     path[0].getSubElements(), currentMenu, direction);
504
505         if (nextMenu != null && nextMenu != currentMenu) {
506             MenuElement newSelection[];
507             if (len == 2) {
508                         // menu is selected but its popup not shown
509
newSelection = new MenuElement[2];
510             newSelection[0] = path[0];
511             newSelection[1] = nextMenu;
512             } else {
513                         // menu is selected and its popup is shown
514
newSelection = new MenuElement[3];
515             newSelection[0] = path[0];
516             newSelection[1] = nextMenu;
517             newSelection[2] = ((JMenu)nextMenu).getPopupMenu();
518                     }
519             msm.setSelectedPath(newSelection);
520         }
521         }
522     }
523
524         private void selectItem(boolean direction) {
525             MenuSelectionManager msm = MenuSelectionManager.defaultManager();
526             MenuElement path[] = msm.getSelectedPath();
527             if (path.length < 2) {
528                 return;
529             }
530             int len = path.length;
531
532             if (path[0] instanceof JMenuBar &&
533                 path[1] instanceof JMenu && len == 2) {
534
535                 // a toplevel menu is selected, but its popup not shown.
536
// Show the popup and select the first item
537
JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
538                 MenuElement next =
539                     findEnabledChild(popup.getSubElements(), -1, FORWARD);
540                 MenuElement[] newPath;
541
542                 if (next != null) {
543                     // an enabled item found -- include it in newPath
544
newPath = new MenuElement[4];
545                     newPath[3] = next;
546                 } else {
547                     // menu has no enabled items -- still must show the popup
548
newPath = new MenuElement[3];
549                 }
550                 System.arraycopy(path, 0, newPath, 0, 2);
551                 newPath[2] = popup;
552                 msm.setSelectedPath(newPath);
553
554             } else if (path[len-1] instanceof JPopupMenu &&
555                        path[len-2] instanceof JMenu) {
556
557                 // a menu (not necessarily toplevel) is open and its popup
558
// shown. Select the appropriate menu item
559
JMenu menu = (JMenu)path[len-2];
560                 JPopupMenu popup = menu.getPopupMenu();
561                 MenuElement next =
562                     findEnabledChild(popup.getSubElements(), -1, direction);
563
564                 if (next != null) {
565                     MenuElement[] newPath = new MenuElement[len+1];
566                     System.arraycopy(path, 0, newPath, 0, len);
567                     newPath[len] = next;
568                     msm.setSelectedPath(newPath);
569                 } else {
570                     // all items in the popup are disabled.
571
// We're going to find the parent popup menu and select
572
// its next item. If there's no parent popup menu (i.e.
573
// current menu is toplevel), do nothing
574
if (len > 2 && path[len-3] instanceof JPopupMenu) {
575                         popup = ((JPopupMenu)path[len-3]);
576                         next = findEnabledChild(popup.getSubElements(),
577                                                 menu, direction);
578
579                         if (next != null && next != menu) {
580                             MenuElement[] newPath = new MenuElement[len-1];
581                             System.arraycopy(path, 0, newPath, 0, len-2);
582                             newPath[len-2] = next;
583                             msm.setSelectedPath(newPath);
584                         }
585                     }
586                 }
587
588             } else {
589                 // just select the next item, no path expansion needed
590
MenuElement subs[] = path[len-2].getSubElements();
591                 MenuElement nextChild =
592                     findEnabledChild(subs, path[len-1], direction);
593                 if (nextChild == null) {
594                     nextChild = findEnabledChild(subs, -1, direction);
595                 }
596                 if (nextChild != null) {
597                     path[len-1] = nextChild;
598                     msm.setSelectedPath(path);
599         }
600         }
601     }
602
603         private void cancel() {
604         // 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's
605
// a protected method. The real solution could be to make
606
// firePopupMenuCanceled public and call it directly.
607
JPopupMenu lastPopup = (JPopupMenu)getLastPopup();
608         if (lastPopup != null) {
609         lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
610         }
611
612         MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
613         if(path.length > 4) { /* PENDING(arnaud) Change this to 2 when a mouse grabber is available for MenuBar */
614         MenuElement newPath[] = new MenuElement[path.length - 2];
615         System.arraycopy(path,0,newPath,0,path.length-2);
616         MenuSelectionManager.defaultManager().setSelectedPath(newPath);
617         } else
618         MenuSelectionManager.defaultManager().clearSelectedPath();
619     }
620     }
621
622     private static MenuElement nextEnabledChild(MenuElement e[],
623                                                 int fromIndex, int toIndex) {
624     for (int i=fromIndex; i<=toIndex; i++) {
625         if (e[i] != null) {
626         Component JavaDoc comp = e[i].getComponent();
627         if (comp != null && comp.isEnabled() && comp.isVisible()) {
628                     return e[i];
629                 }
630         }
631     }
632     return null;
633     }
634
635     private static MenuElement previousEnabledChild(MenuElement e[],
636                                                 int fromIndex, int toIndex) {
637     for (int i=fromIndex; i>=toIndex; i--) {
638         if (e[i] != null) {
639         Component JavaDoc comp = e[i].getComponent();
640         if (comp != null && comp.isEnabled() && comp.isVisible()) {
641                     return e[i];
642                 }
643         }
644     }
645     return null;
646     }
647
648     static MenuElement findEnabledChild(MenuElement e[], int fromIndex,
649                                                 boolean forward) {
650         MenuElement result = null;
651         if (forward) {
652             result = nextEnabledChild(e, fromIndex+1, e.length-1);
653             if (result == null) result = nextEnabledChild(e, 0, fromIndex-1);
654         } else {
655             result = previousEnabledChild(e, fromIndex-1, 0);
656             if (result == null) result = previousEnabledChild(e, e.length-1,
657                                                               fromIndex+1);
658         }
659     return result;
660     }
661
662     static MenuElement findEnabledChild(MenuElement e[],
663                                    MenuElement elem, boolean forward) {
664         for (int i=0; i<e.length; i++) {
665             if (e[i] == elem) {
666                 return findEnabledChild(e, i, forward);
667             }
668         }
669         return null;
670     }
671
672     private transient static MouseGrabber mouseGrabber = null;
673
674     private static class MouseGrabber implements ChangeListener JavaDoc,
675         AWTEventListener, ComponentListener, WindowListener {
676
677         Window JavaDoc grabbedWindow;
678     MenuElement[] lastPathSelected;
679
680         public MouseGrabber() {
681         MenuSelectionManager msm = MenuSelectionManager.defaultManager();
682         msm.addChangeListener(this);
683             this.lastPathSelected = msm.getSelectedPath();
684             if(this.lastPathSelected.length != 0) {
685                 grabWindow(this.lastPathSelected);
686             }
687         }
688
689         void grabWindow(MenuElement[] newPath) {
690             // A grab needs to be added
691
java.security.AccessController.doPrivileged(
692                 new java.security.PrivilegedAction JavaDoc() {
693                     public Object JavaDoc run() {
694                         Toolkit.getDefaultToolkit()
695                             .addAWTEventListener(MouseGrabber.this,
696                                 AWTEvent.MOUSE_EVENT_MASK |
697                                 AWTEvent.MOUSE_MOTION_EVENT_MASK |
698                                 AWTEvent.MOUSE_WHEEL_EVENT_MASK);
699                         return null;
700                     }
701                 }
702             );
703
704             Component JavaDoc invoker = newPath[0].getComponent();
705             if (invoker instanceof JPopupMenu) {
706                 invoker = ((JPopupMenu)invoker).getInvoker();
707             }
708             grabbedWindow = invoker instanceof Window JavaDoc?
709                     (Window JavaDoc)invoker :
710                     SwingUtilities.getWindowAncestor(invoker);
711             if(grabbedWindow != null) {
712                 grabbedWindow.addComponentListener(this);
713                 grabbedWindow.addWindowListener(this);
714             }
715         }
716
717         void ungrabWindow() {
718             // The grab should be removed
719
java.security.AccessController.doPrivileged(
720                 new java.security.PrivilegedAction JavaDoc() {
721                     public Object JavaDoc run() {
722                         Toolkit.getDefaultToolkit()
723                             .removeAWTEventListener(MouseGrabber.this);
724                         return null;
725                     }
726                 }
727             );
728             if(grabbedWindow != null) {
729                 grabbedWindow.removeComponentListener(this);
730                 grabbedWindow.removeWindowListener(this);
731                 grabbedWindow = null;
732             }
733         }
734
735     public void stateChanged(ChangeEvent JavaDoc e) {
736         MenuSelectionManager msm = MenuSelectionManager.defaultManager();
737         MenuElement[] p = msm.getSelectedPath();
738
739         if (lastPathSelected.length == 0 && p.length != 0) {
740                 grabWindow(p);
741             }
742
743         if (lastPathSelected.length != 0 && p.length == 0) {
744                 ungrabWindow();
745             }
746
747             lastPathSelected = p;
748         }
749
750         public void eventDispatched(AWTEvent JavaDoc ev) {
751             switch (ev.getID()) {
752             case MouseEvent.MOUSE_PRESSED:
753                 Component JavaDoc src = (Component JavaDoc)ev.getSource();
754                 if (isInPopup(src) ||
755                     (src instanceof JMenu && ((JMenu)src).isSelected())) {
756                     return;
757                 }
758                 if (!(src instanceof JComponent) ||
759                    ! (((JComponent)src).getClientProperty("doNotCancelPopup")
760                          == BasicComboBoxUI.HIDE_POPUP_KEY)) {
761                     // Cancel popup only if this property was not set.
762
// If this property is set to TRUE component wants
763
// to deal with this event by himself.
764
cancelPopupMenu();
765                     // Ask UIManager about should we consume event that closes
766
// popup. This made to match native apps behaviour.
767
boolean consumeEvent =
768                         UIManager.getBoolean("PopupMenu.consumeEventOnClose");
769                     // Consume the event so that normal processing stops.
770
if(consumeEvent && !(src instanceof MenuElement)) {
771                         ((MouseEvent)ev).consume();
772                     }
773                 }
774                 break;
775
776             case MouseEvent.MOUSE_RELEASED:
777                 src = (Component JavaDoc)ev.getSource();
778                 if(!(src instanceof MenuElement)) {
779                     // Do not forward event to MSM, let component handle it
780
break;
781                 }
782                 if(src instanceof JMenu || !(src instanceof JMenuItem)) {
783                     MenuSelectionManager.defaultManager().
784                         processMouseEvent((MouseEvent)ev);
785                 }
786                 break;
787             case MouseEvent.MOUSE_DRAGGED:
788                 src = (Component JavaDoc)ev.getSource();
789                 if(!(src instanceof MenuElement)) {
790                     // Do not forward event to MSM, let component handle it
791
break;
792                 }
793                 MenuSelectionManager.defaultManager().
794                     processMouseEvent((MouseEvent)ev);
795                 break;
796             case MouseEvent.MOUSE_WHEEL:
797                 if (isInPopup((Component JavaDoc)ev.getSource())) {
798                     return;
799                 }
800                 cancelPopupMenu();
801                 break;
802             }
803         }
804
805         boolean isInPopup(Component JavaDoc src) {
806             for (Component JavaDoc c=src; c!=null; c=c.getParent()) {
807                 if (c instanceof Applet JavaDoc || c instanceof Window JavaDoc) {
808                     break;
809                 } else if (c instanceof JPopupMenu) {
810                     return true;
811                 }
812             }
813             return false;
814         }
815
816         void cancelPopupMenu() {
817         JPopupMenu firstPopup = (JPopupMenu)getFirstPopup();
818             // 4234793: This action should call firePopupMenuCanceled but it's
819
// a protected method. The real solution could be to make
820
// firePopupMenuCanceled public and call it directly.
821
List popups = getPopups();
822             Iterator iter = popups.iterator();
823             while (iter.hasNext()) {
824                 JPopupMenu popup = (JPopupMenu)iter.next();
825                 popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
826             }
827             MenuSelectionManager.defaultManager().clearSelectedPath();
828         }
829
830         public void componentResized(ComponentEvent e) {
831             cancelPopupMenu();
832         }
833         public void componentMoved(ComponentEvent e) {
834             cancelPopupMenu();
835         }
836         public void componentShown(ComponentEvent e) {
837             cancelPopupMenu();
838         }
839         public void componentHidden(ComponentEvent e) {
840             cancelPopupMenu();
841         }
842         public void windowClosing(WindowEvent e) {
843             cancelPopupMenu();
844         }
845         public void windowClosed(WindowEvent e) {
846             cancelPopupMenu();
847         }
848         public void windowIconified(WindowEvent e) {
849             cancelPopupMenu();
850         }
851         public void windowDeactivated(WindowEvent e) {
852             if(doUnpostPopupOnDeactivation()) {
853                 cancelPopupMenu();
854             }
855         }
856         public void windowOpened(WindowEvent e) {}
857         public void windowDeiconified(WindowEvent e) {}
858         public void windowActivated(WindowEvent e) {}
859     }
860
861     /**
862      * This helper is added to MenuSelectionManager as a ChangeListener to
863      * listen to menu selection changes. When a menu is activated, it passes
864      * focus to its parent JRootPane, and installs an ActionMap/InputMap pair
865      * on that JRootPane. Those maps are necessary in order for menu
866      * navigation to work. When menu is being deactivated, it restores focus
867      * to the component that has had it before menu activation, and uninstalls
868      * the maps.
869      * This helper is also installed as a KeyListener on root pane when menu
870      * is active. It forwards key events to MenuSelectionManager for mnemonic
871      * keys handling.
872      */

873     private static class MenuKeyboardHelper
874         implements ChangeListener JavaDoc, KeyListener {
875
876         private Component JavaDoc lastFocused = null;
877     private MenuElement[] lastPathSelected = new MenuElement[0];
878         private JPopupMenu lastPopup;
879
880         private JRootPane invokerRootPane;
881         private ActionMap menuActionMap = getActionMap();
882         private InputMap menuInputMap;
883         private boolean focusTraversalKeysEnabled;
884
885         /*
886          * Fix for 4213634
887          * If this is false, KEY_TYPED and KEY_RELEASED events are NOT
888          * processed. This is needed to avoid activating a menuitem when
889          * the menu and menuitem share the same mnemonic.
890          */

891         private boolean receivedKeyPressed = false;
892
893         void removeItems() {
894             if (lastFocused != null) {
895                 if(!lastFocused.requestFocusInWindow()) {
896                     // Workarounr for 4810575.
897
// If lastFocused is not in currently focused window
898
// requestFocusInWindow will fail. In this case we must
899
// request focus by requestFocus() if it was not
900
// transferred from our popup.
901
Window JavaDoc cfw = KeyboardFocusManager
902                                  .getCurrentKeyboardFocusManager()
903                                   .getFocusedWindow();
904                     if(cfw != null &&
905                        "###focusableSwingPopup###".equals(cfw.getName())) {
906                         lastFocused.requestFocus();
907                     }
908
909                 }
910                 lastFocused = null;
911             }
912             if (invokerRootPane != null) {
913                 invokerRootPane.removeKeyListener(menuKeyboardHelper);
914                 invokerRootPane.setFocusTraversalKeysEnabled(focusTraversalKeysEnabled);
915                 removeUIInputMap(invokerRootPane, menuInputMap);
916                 removeUIActionMap(invokerRootPane, menuActionMap);
917                 invokerRootPane = null;
918             }
919             receivedKeyPressed = false;
920         }
921
922         private FocusListener rootPaneFocusListener = new FocusAdapter() {
923                 public void focusGained(FocusEvent ev) {
924                     Component JavaDoc opposite = ev.getOppositeComponent();
925                     if (opposite != null) {
926                         lastFocused = opposite;
927                     }
928                     ev.getComponent().removeFocusListener(this);
929                 }
930             };
931
932         /**
933          * Return the last JPopupMenu in <code>path</code>,
934          * or <code>null</code> if none found
935          */

936         JPopupMenu getActivePopup(MenuElement[] path) {
937             for (int i=path.length-1; i>=0; i--) {
938                 MenuElement elem = path[i];
939                 if (elem instanceof JPopupMenu) {
940                     return (JPopupMenu)elem;
941                 }
942             }
943             return null;
944         }
945
946         void addUIInputMap(JComponent c, InputMap map) {
947             InputMap lastNonUI = null;
948             InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
949
950             while (parent != null && !(parent instanceof UIResource)) {
951                 lastNonUI = parent;
952                 parent = parent.getParent();
953             }
954
955             if (lastNonUI == null) {
956                 c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map);
957             } else {
958                 lastNonUI.setParent(map);
959             }
960             map.setParent(parent);
961         }
962
963         void addUIActionMap(JComponent c, ActionMap map) {
964             ActionMap lastNonUI = null;
965             ActionMap parent = c.getActionMap();
966
967             while (parent != null && !(parent instanceof UIResource)) {
968                 lastNonUI = parent;
969                 parent = parent.getParent();
970             }
971
972             if (lastNonUI == null) {
973                 c.setActionMap(map);
974             } else {
975                 lastNonUI.setParent(map);
976             }
977             map.setParent(parent);
978         }
979
980         void removeUIInputMap(JComponent c, InputMap map) {
981             InputMap im = null;
982             InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
983
984             while (parent != null) {
985                 if (parent == map) {
986                     if (im == null) {
987                         c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,
988                                       map.getParent());
989                     } else {
990                         im.setParent(map.getParent());
991                     }
992                     break;
993                 }
994                 im = parent;
995                 parent = parent.getParent();
996             }
997         }
998
999         void removeUIActionMap(JComponent c, ActionMap map) {
1000            ActionMap im = null;
1001            ActionMap parent = c.getActionMap();
1002
1003            while (parent != null) {
1004                if (parent == map) {
1005                    if (im == null) {
1006                        c.setActionMap(map.getParent());
1007                    } else {
1008                        im.setParent(map.getParent());
1009                    }
1010                    break;
1011                }
1012                im = parent;
1013                parent = parent.getParent();
1014            }
1015        }
1016
1017        public void stateChanged(ChangeEvent JavaDoc ev) {
1018            if (!(UIManager.getLookAndFeel() instanceof BasicLookAndFeel JavaDoc)) {
1019                MenuSelectionManager msm = MenuSelectionManager.
1020                                           defaultManager();
1021                msm.removeChangeListener(this);
1022                menuKeyboardHelperInstalled = false;
1023                return;
1024            }
1025        MenuSelectionManager msm = (MenuSelectionManager)ev.getSource();
1026        MenuElement[] p = msm.getSelectedPath();
1027            JPopupMenu popup = getActivePopup(p);
1028            if (popup != null && !popup.isFocusable()) {
1029                // Do nothing for non-focusable popups
1030
return;
1031            }
1032
1033            if (lastPathSelected.length != 0 && p.length != 0 ) {
1034                if (!checkInvokerEqual(p[0],lastPathSelected[0])) {
1035                        removeItems();
1036                        lastPathSelected = new MenuElement[0];
1037                        }
1038
1039            }
1040
1041
1042
1043
1044        if (lastPathSelected.length == 0 && p.length > 0) {
1045                // menu posted
1046
JComponent invoker;
1047
1048                if (popup == null) {
1049                    if (p.length == 2 && p[0] instanceof JMenuBar &&
1050                        p[1] instanceof JMenu) {
1051                        // a menu has been selected but not open
1052
invoker = (JComponent)p[1];
1053                        popup = ((JMenu)invoker).getPopupMenu();
1054                    } else {
1055                        return;
1056                    }
1057                } else {
1058                    Component JavaDoc c = popup.getInvoker();
1059                    if(c instanceof JFrame) {
1060                        invoker = ((JFrame)c).getRootPane();
1061                    } else if(c instanceof JApplet) {
1062                        invoker = ((JApplet)c).getRootPane();
1063                    } else {
1064                        while (!(c instanceof JComponent)) {
1065                            if (c == null) {
1066                                return;
1067                            }
1068                            c = c.getParent();
1069                        }
1070                        invoker = (JComponent)c;
1071                    }
1072                }
1073
1074                // remember current focus owner
1075
lastFocused = KeyboardFocusManager.
1076                    getCurrentKeyboardFocusManager().getFocusOwner();
1077
1078                // request focus on root pane and install keybindings
1079
// used for menu navigation
1080
invokerRootPane = SwingUtilities.getRootPane(invoker);
1081                if (invokerRootPane != null) {
1082                    invokerRootPane.addFocusListener(rootPaneFocusListener);
1083                    invokerRootPane.requestFocus(true);
1084                    invokerRootPane.addKeyListener(menuKeyboardHelper);
1085                    focusTraversalKeysEnabled = invokerRootPane.
1086                                      getFocusTraversalKeysEnabled();
1087                    invokerRootPane.setFocusTraversalKeysEnabled(false);
1088
1089                    menuInputMap = getInputMap(popup, invokerRootPane);
1090                    addUIInputMap(invokerRootPane, menuInputMap);
1091                    addUIActionMap(invokerRootPane, menuActionMap);
1092                }
1093            } else if (lastPathSelected.length != 0 && p.length == 0) {
1094        // menu hidden -- return focus to where it had been before
1095
// and uninstall menu keybindings
1096
removeItems();
1097        } else {
1098                if (popup != lastPopup) {
1099                    receivedKeyPressed = false;
1100                }
1101            }
1102
1103            // Remember the last path selected
1104
lastPathSelected = p;
1105            lastPopup = popup;
1106        }
1107
1108        public void keyPressed(KeyEvent ev) {
1109            receivedKeyPressed = true;
1110            MenuSelectionManager.defaultManager().processKeyEvent(ev);
1111        }
1112
1113        public void keyReleased(KeyEvent ev) {
1114        if (receivedKeyPressed) {
1115        receivedKeyPressed = false;
1116                MenuSelectionManager.defaultManager().processKeyEvent(ev);
1117            }
1118        }
1119
1120        public void keyTyped(KeyEvent ev) {
1121        if (receivedKeyPressed) {
1122                MenuSelectionManager.defaultManager().processKeyEvent(ev);
1123            }
1124        }
1125    }
1126}
1127
Popular Tags