1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48:
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63: import ;
64: import ;
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74:
75:
78: public class BasicPopupMenuUI extends PopupMenuUI
79: {
80:
83: private static class NavigateAction
84: extends AbstractAction
85: {
86:
87:
92: NavigateAction(String name)
93: {
94: super(name);
95: }
96:
97:
100: public void actionPerformed(ActionEvent event)
101: {
102: String name = (String) getValue(Action.NAME);
103: if (name.equals("selectNext"))
104: navigateNextPrevious(true);
105: else if (name.equals("selectPrevious"))
106: navigateNextPrevious(false);
107: else if (name.equals("selectChild"))
108: navigateParentChild(true);
109: else if (name.equals("selectParent"))
110: navigateParentChild(false);
111: else if (name.equals("cancel"))
112: cancel();
113: else if (name.equals("return"))
114: doReturn();
115: else
116: assert false : "Must not reach here";
117: }
118:
119:
125: private void navigateNextPrevious(boolean dir)
126: {
127: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
128: MenuElement path[] = msm.getSelectedPath();
129: int len = path.length;
130: if (len >= 2)
131: {
132:
133: if (path[0] instanceof JMenuBar &&
134: path[1] instanceof JMenu && len == 2)
135: {
136:
137:
138:
139: JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
140: MenuElement next =
141: findEnabledChild(popup.getSubElements(), -1, true);
142: MenuElement[] newPath;
143:
144: if (next != null)
145: {
146: newPath = new MenuElement[4];
147: newPath[3] = next;
148: }
149: else
150: {
151:
152: newPath = new MenuElement[3];
153: }
154: System.arraycopy(path, 0, newPath, 0, 2);
155: newPath[2] = popup;
156: msm.setSelectedPath(newPath);
157: }
158: else if (path[len - 1] instanceof JPopupMenu &&
159: path[len - 2] instanceof JMenu)
160: {
161:
162: JMenu menu = (JMenu) path[len - 2];
163: JPopupMenu popup = menu.getPopupMenu();
164: MenuElement next =
165: findEnabledChild(popup.getSubElements(), -1, dir);
166:
167: if (next != null)
168: {
169: MenuElement[] newPath = new MenuElement[len + 1];
170: System.arraycopy(path, 0, newPath, 0, len);
171: newPath[len] = next;
172: msm.setSelectedPath(newPath);
173: }
174: else
175: {
176:
177:
178:
179: if (len > 2 && path[len - 3] instanceof JPopupMenu)
180: {
181: popup = ((JPopupMenu) path[len - 3]);
182: next = findEnabledChild(popup.getSubElements(),
183: menu, dir);
184: if (next != null && next != menu)
185: {
186: MenuElement[] newPath = new MenuElement[len - 1];
187: System.arraycopy(path, 0, newPath, 0, len - 2);
188: newPath[len - 2] = next;
189: msm.setSelectedPath(newPath);
190: }
191: }
192: }
193: }
194: else
195: {
196:
197: MenuElement subs[] = path[len - 2].getSubElements();
198: MenuElement nextChild =
199: findEnabledChild(subs, path[len - 1], dir);
200: if (nextChild == null)
201: {
202: nextChild = findEnabledChild(subs, -1, dir);
203: }
204: if (nextChild != null)
205: {
206: path[len-1] = nextChild;
207: msm.setSelectedPath(path);
208: }
209: }
210: }
211: }
212:
213: private MenuElement findEnabledChild(MenuElement[] children,
214: MenuElement start, boolean dir)
215: {
216: MenuElement found = null;
217: for (int i = 0; i < children.length && found == null; i++)
218: {
219: if (children[i] == start)
220: {
221: found = findEnabledChild(children, i, dir);
222: }
223: }
224: return found;
225: }
226:
227:
236: private MenuElement findEnabledChild(MenuElement[] children,
237: int start, boolean dir)
238: {
239: MenuElement result = null;
240: if (dir)
241: {
242: result = findNextEnabledChild(children, start + 1, children.length-1);
243: if (result == null)
244: result = findNextEnabledChild(children, 0, start - 1);
245: }
246: else
247: {
248: result = findPreviousEnabledChild(children, start - 1, 0);
249: if (result == null)
250: result = findPreviousEnabledChild(children, children.length-1,
251: start + 1);
252: }
253: return result;
254: }
255:
256:
265: private MenuElement findNextEnabledChild(MenuElement[] children, int start,
266: int end)
267: {
268: MenuElement found = null;
269: for (int i = start; i <= end && found == null; i++)
270: {
271: if (children[i] != null)
272: {
273: Component comp = children[i].getComponent();
274: if (comp != null && comp.isEnabled() && comp.isVisible())
275: {
276: found = children[i];
277: }
278: }
279: }
280: return found;
281: }
282:
283:
292: private MenuElement findPreviousEnabledChild(MenuElement[] children,
293: int start, int end)
294: {
295: MenuElement found = null;
296: for (int i = start; i >= end && found == null; i--)
297: {
298: if (children[i] != null)
299: {
300: Component comp = children[i].getComponent();
301: if (comp != null && comp.isEnabled() && comp.isVisible())
302: {
303: found = children[i];
304: }
305: }
306: }
307: return found;
308: }
309:
310:
316: private void navigateParentChild(boolean selectChild)
317: {
318: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
319: MenuElement path[] = msm.getSelectedPath();
320: int len = path.length;
321:
322: if (selectChild)
323: {
324: if (len > 0 && path[len - 1] instanceof JMenu
325: && ! ((JMenu) path[len-1]).isTopLevelMenu())
326: {
327:
328: JMenu menu = (JMenu) path[len - 1];
329: JPopupMenu popup = menu.getPopupMenu();
330: MenuElement[] subs = popup.getSubElements();
331: MenuElement item = findEnabledChild(subs, -1, true);
332: MenuElement[] newPath;
333:
334: if (item == null)
335: {
336: newPath = new MenuElement[len + 1];
337: }
338: else
339: {
340: newPath = new MenuElement[len + 2];
341: newPath[len + 1] = item;
342: }
343: System.arraycopy(path, 0, newPath, 0, len);
344: newPath[len] = popup;
345: msm.setSelectedPath(newPath);
346: return;
347: }
348: }
349: else
350: {
351: int popupIndex = len-1;
352: if (len > 2
353: && (path[popupIndex] instanceof JPopupMenu
354: || path[--popupIndex] instanceof JPopupMenu)
355: && ! ((JMenu) path[popupIndex - 1]).isTopLevelMenu())
356: {
357:
358: MenuElement newPath[] = new MenuElement[popupIndex];
359: System.arraycopy(path, 0, newPath, 0, popupIndex);
360: msm.setSelectedPath(newPath);
361: return;
362: }
363: }
364:
365:
366:
367:
368: if (len > 1 && path[0] instanceof JMenuBar)
369: {
370: MenuElement currentMenu = path[1];
371: MenuElement nextMenu = findEnabledChild(path[0].getSubElements(),
372: currentMenu, selectChild);
373:
374: if (nextMenu != null && nextMenu != currentMenu)
375: {
376: MenuElement newSelection[];
377: if (len == 2)
378: {
379:
380: newSelection = new MenuElement[2];
381: newSelection[0] = path[0];
382: newSelection[1] = nextMenu;
383: }
384: else
385: {
386:
387: newSelection = new MenuElement[3];
388: newSelection[0] = path[0];
389: newSelection[1] = nextMenu;
390: newSelection[2] = ((JMenu) nextMenu).getPopupMenu();
391: }
392: msm.setSelectedPath(newSelection);
393: }
394: }
395: }
396:
397:
400: private void cancel()
401: {
402:
403:
404:
405:
406: JPopupMenu lastPopup = (JPopupMenu) getLastPopup();
407: EventListener[] ll = lastPopup.getListeners(PopupMenuListener.class);
408: for (int i = 0; i < ll.length; i++)
409: {
410: PopupMenuEvent ev = new PopupMenuEvent(lastPopup);
411: ((PopupMenuListener) ll[i]).popupMenuCanceled(ev);
412: }
413:
414:
415:
416: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
417: MenuElement path[] = msm.getSelectedPath();
418: if(path.length > 4)
419: {
420: MenuElement newPath[] = new MenuElement[path.length - 2];
421: System.arraycopy(path,0,newPath,0,path.length-2);
422: MenuSelectionManager.defaultManager().setSelectedPath(newPath);
423: }
424: else
425: msm.clearSelectedPath();
426: }
427:
428:
433: private JPopupMenu getLastPopup()
434: {
435: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
436: MenuElement[] p = msm.getSelectedPath();
437: JPopupMenu popup = null;
438: for(int i = p.length - 1; popup == null && i >= 0; i--)
439: {
440: if (p[i] instanceof JPopupMenu)
441: popup = (JPopupMenu) p[i];
442: }
443: return popup;
444: }
445:
446:
450: private void doReturn()
451: {
452: KeyboardFocusManager fmgr =
453: KeyboardFocusManager.getCurrentKeyboardFocusManager();
454: Component focusOwner = fmgr.getFocusOwner();
455: if((focusOwner == null || (focusOwner instanceof JRootPane)))
456: {
457: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
458: MenuElement path[] = msm.getSelectedPath();
459: MenuElement lastElement;
460: if(path.length > 0)
461: {
462: lastElement = path[path.length - 1];
463: if(lastElement instanceof JMenu)
464: {
465: MenuElement newPath[] = new MenuElement[path.length + 1];
466: System.arraycopy(path,0,newPath,0,path.length);
467: newPath[path.length] = ((JMenu) lastElement).getPopupMenu();
468: msm.setSelectedPath(newPath);
469: }
470: else if(lastElement instanceof JMenuItem)
471: {
472: JMenuItem mi = (JMenuItem)lastElement;
473: if (mi.getUI() instanceof BasicMenuItemUI)
474: {
475: ((BasicMenuItemUI)mi.getUI()).doClick(msm);
476: }
477: else
478: {
479: msm.clearSelectedPath();
480: mi.doClick(0);
481: }
482: }
483: }
484: }
485: }
486: }
487:
488:
493: private class KeyboardHelper
494: implements ChangeListener
495: {
496: private MenuElement[] lastSelectedPath = new MenuElement[0];
497: private Component lastFocused;
498: private JRootPane invokerRootPane;
499:
500: public void stateChanged(ChangeEvent event)
501: {
502: MenuSelectionManager msm = (MenuSelectionManager) event.getSource();
503: MenuElement[] p = msm.getSelectedPath();
504: JPopupMenu popup = getActivePopup(p);
505: if (popup == null || popup.isFocusable())
506: {
507: if (lastSelectedPath.length != 0 && p.length != 0 )
508: {
509: if (! invokerEquals(p[0], lastSelectedPath[0]))
510: {
511: uninstallKeyboardActionsImpl();
512: lastSelectedPath = new MenuElement[0];
513: }
514: }
515:
516: if (lastSelectedPath.length == 0 && p.length > 0)
517: {
518: JComponent invoker;
519: if (popup == null)
520: {
521: if (p.length == 2 && p[0] instanceof JMenuBar
522: && p[1] instanceof JMenu)
523: {
524:
525: invoker = (JComponent)p[1];
526: popup = ((JMenu)invoker).getPopupMenu();
527: }
528: else
529: {
530: return;
531: }
532: }
533: else
534: {
535: Component c = popup.getInvoker();
536: if(c instanceof JFrame)
537: {
538: invoker = ((JFrame) c).getRootPane();
539: }
540: else if(c instanceof JApplet)
541: {
542: invoker = ((JApplet) c).getRootPane();
543: }
544: else
545: {
546: while (!(c instanceof JComponent))
547: {
548: if (c == null)
549: {
550: return;
551: }
552: c = c.getParent();
553: }
554: invoker = (JComponent)c;
555: }
556: }
557:
558:
559: lastFocused = KeyboardFocusManager.
560: getCurrentKeyboardFocusManager().getFocusOwner();
561:
562:
563: invokerRootPane = SwingUtilities.getRootPane(invoker);
564: if (invokerRootPane != null)
565: {
566: invokerRootPane.requestFocus(true);
567: installKeyboardActionsImpl();
568: }
569: }
570: else if (lastSelectedPath.length != 0 && p.length == 0)
571: {
572:
573:
574: uninstallKeyboardActionsImpl();
575: }
576: }
577:
578:
579: lastSelectedPath = p;
580: }
581:
582: private JPopupMenu getActivePopup(MenuElement[] path)
583: {
584: JPopupMenu active = null;
585: for (int i = path.length - 1; i >= 0 && active == null; i--)
586: {
587: MenuElement elem = path[i];
588: if (elem instanceof JPopupMenu)
589: {
590: active = (JPopupMenu) elem;
591: }
592: }
593: return active;
594: }
595:
596: private boolean invokerEquals(MenuElement el1, MenuElement el2)
597: {
598: Component invoker1 = el1.getComponent();
599: Component invoker2 = el2.getComponent();
600: if (invoker1 instanceof JPopupMenu)
601: invoker1 = ((JPopupMenu) invoker1).getInvoker();
602: if (invoker2 instanceof JPopupMenu)
603: invoker2 = ((JPopupMenu) invoker2).getInvoker();
604: return invoker1 == invoker2;
605: }
606: }
607:
608:
609: protected JPopupMenu popupMenu;
610:
611:
612: private transient PopupMenuListener popupMenuListener;
613:
614:
616: TopWindowListener topWindowListener;
617:
618:
624: private static int numPopups;
625:
626:
629: private static KeyboardHelper keyboardHelper;
630:
631:
634: public BasicPopupMenuUI()
635: {
636: popupMenuListener = new PopupMenuHandler();
637: topWindowListener = new TopWindowListener();
638: }
639:
640:
648: public static ComponentUI createUI(JComponent x)
649: {
650: return new BasicPopupMenuUI();
651: }
652:
653:
660: public void installUI(JComponent c)
661: {
662: super.installUI(c);
663:
664:
665: if (numPopups == 0)
666: {
667: keyboardHelper = new KeyboardHelper();
668: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
669: msm.addChangeListener(keyboardHelper);
670: }
671: numPopups++;
672:
673: popupMenu = (JPopupMenu) c;
674: popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));
675: popupMenu.setBorderPainted(true);
676: JPopupMenu.setDefaultLightWeightPopupEnabled(true);
677:
678: installDefaults();
679: installListeners();
680: installKeyboardActions();
681: }
682:
683:
687: public void installDefaults()
688: {
689: LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background",
690: "PopupMenu.foreground", "PopupMenu.font");
691: LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
692: popupMenu.setOpaque(true);
693: }
694:
695:
698: protected void installListeners()
699: {
700: popupMenu.addPopupMenuListener(popupMenuListener);
701: }
702:
703:
706: protected void installKeyboardActions()
707: {
708:
709:
710:
711:
712:
713: }
714:
715:
718: void installKeyboardActionsImpl()
719: {
720: Object[] bindings;
721: if (popupMenu.getComponentOrientation().isLeftToRight())
722: {
723: bindings = (Object[])
724: SharedUIDefaults.get("PopupMenu.selectedWindowInputMapBindings");
725: }
726: else
727: {
728: bindings = (Object[]) SharedUIDefaults.get
729: ("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
730: }
731: InputMap inputMap = LookAndFeel.makeComponentInputMap(popupMenu, bindings);
732: SwingUtilities.replaceUIInputMap(popupMenu,
733: JComponent.WHEN_IN_FOCUSED_WINDOW,
734: inputMap);
735:
736:
737: SwingUtilities.replaceUIActionMap(popupMenu, getActionMap());
738: }
739:
740:
745: private ActionMap getActionMap()
746: {
747: ActionMap am = (ActionMap) UIManager.get("PopupMenu.actionMap");
748: if (am == null)
749: {
750: am = createDefaultActions();
751: UIManager.getLookAndFeelDefaults().put("PopupMenu.actionMap", am);
752: }
753: return am;
754: }
755:
756:
761: private ActionMap createDefaultActions()
762: {
763: ActionMapUIResource am = new ActionMapUIResource();
764: Action action = new NavigateAction("selectNext");
765: am.put(action.getValue(Action.NAME), action);
766: action = new NavigateAction("selectPrevious");
767: am.put(action.getValue(Action.NAME), action);
768: action = new NavigateAction("selectParent");
769: am.put(action.getValue(Action.NAME), action);
770: action = new NavigateAction("selectChild");
771: am.put(action.getValue(Action.NAME), action);
772: action = new NavigateAction("return");
773: am.put(action.getValue(Action.NAME), action);
774: action = new NavigateAction("cancel");
775: am.put(action.getValue(Action.NAME), action);
776:
777: return am;
778: }
779:
780:
787: public void uninstallUI(JComponent c)
788: {
789: uninstallListeners();
790: uninstallDefaults();
791: uninstallKeyboardActions();
792: popupMenu = null;
793:
794:
795: numPopups--;
796: if (numPopups == 0)
797: {
798: MenuSelectionManager msm = MenuSelectionManager.defaultManager();
799: msm.removeChangeListener(keyboardHelper);
800: }
801:
802: }
803:
804:
808: protected void uninstallDefaults()
809: {
810: popupMenu.setBackground(null);
811: popupMenu.setBorder(null);
812: popupMenu.setFont(null);
813: popupMenu.setForeground(null);
814: }
815:
816:
819: protected void uninstallListeners()
820: {
821: popupMenu.removePopupMenuListener(popupMenuListener);
822: }
823:
824:
827: protected void uninstallKeyboardActions()
828: {
829:
830:
831:
832:
833:
834: }
835:
836:
839: void uninstallKeyboardActionsImpl()
840: {
841: SwingUtilities.replaceUIInputMap(popupMenu,
842: JComponent.WHEN_IN_FOCUSED_WINDOW, null);
843: SwingUtilities.replaceUIActionMap(popupMenu, null);
844: }
845:
846:
853: public Dimension getMinimumSize(JComponent c)
854: {
855: return null;
856: }
857:
858:
865: public Dimension getPreferredSize(JComponent c)
866: {
867: return null;
868: }
869:
870:
877: public Dimension getMaximumSize(JComponent c)
878: {
879: return null;
880: }
881:
882:
891: public boolean isPopupTrigger(MouseEvent e)
892: {
893: return false;
894: }
895:
896:
899: private class PopupMenuHandler implements PopupMenuListener
900: {
901:
906: public void popupMenuCanceled(PopupMenuEvent event)
907: {
908: MenuSelectionManager manager = MenuSelectionManager.defaultManager();
909: manager.clearSelectedPath();
910: }
911:
912:
917: public void popupMenuWillBecomeInvisible(PopupMenuEvent event)
918: {
919:
920:
921: Component invoker = popupMenu.getInvoker();
922: Component rootContainer = SwingUtilities.getRoot(invoker);
923: if (rootContainer != null)
924: rootContainer.removeComponentListener(topWindowListener);
925: }
926:
927:
932: public void popupMenuWillBecomeVisible(PopupMenuEvent event)
933: {
934:
935:
936:
937: Component invoker = popupMenu.getInvoker();
938: Component rootContainer = SwingUtilities.getRoot(invoker);
939: if (rootContainer != null)
940: rootContainer.addComponentListener(topWindowListener);
941:
942:
943:
944:
945: MenuSelectionManager manager = MenuSelectionManager.defaultManager();
946:
947: if (manager.getSelectedPath().length == 0)
948: {
949:
950: MenuElement[] path = new MenuElement[2];
951: path[0] = popupMenu;
952: Component[] comps = popupMenu.getComponents();
953: if (comps.length != 0 && comps[0] instanceof MenuElement)
954: {
955: path[1] = (MenuElement) comps[0];
956: manager.setSelectedPath(path);
957: }
958: }
959: }
960: }
961:
962:
968: private class TopWindowListener implements ComponentListener
969: {
970:
976: public void componentResized(ComponentEvent e)
977: {
978: MenuSelectionManager manager = MenuSelectionManager.defaultManager();
979: manager.clearSelectedPath();
980: }
981:
982:
988: public void componentMoved(ComponentEvent e)
989: {
990: MenuSelectionManager manager = MenuSelectionManager.defaultManager();
991: manager.clearSelectedPath();
992: }
993:
994:
1000: public void componentShown(ComponentEvent e)
1001: {
1002: MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1003: manager.clearSelectedPath();
1004: }
1005:
1006:
1012: public void componentHidden(ComponentEvent e)
1013: {
1014: MenuSelectionManager manager = MenuSelectionManager.defaultManager();
1015: manager.clearSelectedPath();
1016: }
1017: }
1018:
1019: }