1 /*
2 * Copyright (c) 1997, 2014, 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
310 /**
311 * Handles mnemonic for children JMenuItems.
312 * @since 1.5
313 */
314 private class BasicMenuKeyListener implements MenuKeyListener {
315 MenuElement menuToOpen = null;
316
317 public void menuKeyTyped(MenuKeyEvent e) {
318 if (menuToOpen != null) {
319 // we have a submenu to open
320 JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu();
321 MenuElement subitem = findEnabledChild(
322 subpopup.getSubElements(), -1, true);
323
324 ArrayList<MenuElement> lst = new ArrayList<MenuElement>(Arrays.asList(e.getPath()));
325 lst.add(menuToOpen);
326 lst.add(subpopup);
327 if (subitem != null) {
328 lst.add(subitem);
329 }
330 MenuElement newPath[] = new MenuElement[0];
331 newPath = lst.toArray(newPath);
332 MenuSelectionManager.defaultManager().setSelectedPath(newPath);
333 e.consume();
334 }
335 menuToOpen = null;
336 }
337
338 public void menuKeyPressed(MenuKeyEvent e) {
339 char keyChar = e.getKeyChar();
340
341 // Handle the case for Escape or Enter...
342 if (!Character.isLetterOrDigit(keyChar)) {
343 return;
344 }
345
346 MenuSelectionManager manager = e.getMenuSelectionManager();
347 MenuElement path[] = e.getPath();
348 MenuElement items[] = popupMenu.getSubElements();
349 int currentIndex = -1;
350 int matches = 0;
351 int firstMatch = -1;
352 int indexes[] = null;
353
354 for (int j = 0; j < items.length; j++) {
355 if (! (items[j] instanceof JMenuItem)) {
356 continue;
357 }
358 JMenuItem item = (JMenuItem)items[j];
359 int mnemonic = item.getMnemonic();
360 if (item.isEnabled() &&
361 item.isVisible() && lower(keyChar) == lower(mnemonic)) {
362 if (matches == 0) {
363 firstMatch = j;
364 matches++;
365 } else {
366 if (indexes == null) {
367 indexes = new int[items.length];
368 indexes[0] = firstMatch;
369 }
370 indexes[matches++] = j;
371 }
372 }
380 } else if (matches == 1) {
381 // Invoke the menu action
382 JMenuItem item = (JMenuItem)items[firstMatch];
383 if (item instanceof JMenu) {
384 // submenus are handled in menuKeyTyped
385 menuToOpen = item;
386 } else if (item.isEnabled()) {
387 // we have a menu item
388 manager.clearSelectedPath();
389 item.doClick();
390 }
391 e.consume();
392 } else {
393 // Select the menu item with the matching mnemonic. If
394 // the same mnemonic has been invoked then select the next
395 // menu item in the cycle.
396 MenuElement newItem;
397
398 newItem = items[indexes[(currentIndex + 1) % matches]];
399
400 MenuElement newPath[] = new MenuElement[path.length+1];
401 System.arraycopy(path, 0, newPath, 0, path.length);
402 newPath[path.length] = newItem;
403 manager.setSelectedPath(newPath);
404 e.consume();
405 }
406 }
407
408 public void menuKeyReleased(MenuKeyEvent e) {
409 }
410
411 private char lower(char keyChar) {
412 return Character.toLowerCase(keyChar);
413 }
414
415 private char lower(int mnemonic) {
416 return Character.toLowerCase((char) mnemonic);
417 }
418 }
419
420 private static class Actions extends UIAction {
453 else if (key == SELECT_PARENT) {
454 selectParentChild(PARENT);
455 }
456 else if (key == SELECT_CHILD) {
457 selectParentChild(CHILD);
458 }
459 else if (key == RETURN) {
460 doReturn();
461 }
462 }
463
464 private void doReturn() {
465 KeyboardFocusManager fmgr =
466 KeyboardFocusManager.getCurrentKeyboardFocusManager();
467 Component focusOwner = fmgr.getFocusOwner();
468 if(focusOwner != null && !(focusOwner instanceof JRootPane)) {
469 return;
470 }
471
472 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
473 MenuElement path[] = msm.getSelectedPath();
474 MenuElement lastElement;
475 if(path.length > 0) {
476 lastElement = path[path.length-1];
477 if(lastElement instanceof JMenu) {
478 MenuElement newPath[] = new MenuElement[path.length+1];
479 System.arraycopy(path,0,newPath,0,path.length);
480 newPath[path.length] = ((JMenu)lastElement).getPopupMenu();
481 msm.setSelectedPath(newPath);
482 } else if(lastElement instanceof JMenuItem) {
483 JMenuItem mi = (JMenuItem)lastElement;
484
485 if (mi.getUI() instanceof BasicMenuItemUI) {
486 ((BasicMenuItemUI)mi.getUI()).doClick(msm);
487 }
488 else {
489 msm.clearSelectedPath();
490 mi.doClick(0);
491 }
492 }
493 }
494 }
495 private void selectParentChild(boolean direction) {
496 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
497 MenuElement path[] = msm.getSelectedPath();
498 int len = path.length;
499
500 if (direction == PARENT) {
501 // selecting parent
502 int popupIndex = len-1;
503
504 if (len > 2 &&
505 // check if we have an open submenu. A submenu item may or
506 // may not be selected, so submenu popup can be either the
507 // last or next to the last item.
508 (path[popupIndex] instanceof JPopupMenu ||
509 path[--popupIndex] instanceof JPopupMenu) &&
510 !((JMenu)path[popupIndex-1]).isTopLevelMenu()) {
511
512 // we have a submenu, just close it
513 MenuElement newPath[] = new MenuElement[popupIndex];
514 System.arraycopy(path, 0, newPath, 0, popupIndex);
515 msm.setSelectedPath(newPath);
516 return;
517 }
518 } else {
519 // selecting child
520 if (len > 0 && path[len-1] instanceof JMenu &&
521 !((JMenu)path[len-1]).isTopLevelMenu()) {
522
523 // we have a submenu, open it
524 JMenu menu = (JMenu)path[len-1];
525 JPopupMenu popup = menu.getPopupMenu();
526 MenuElement[] subs = popup.getSubElements();
527 MenuElement item = findEnabledChild(subs, -1, true);
528 MenuElement[] newPath;
529
530 if (item == null) {
531 newPath = new MenuElement[len+1];
532 } else {
533 newPath = new MenuElement[len+2];
534 newPath[len+1] = item;
535 }
536 System.arraycopy(path, 0, newPath, 0, len);
537 newPath[len] = popup;
538 msm.setSelectedPath(newPath);
539 return;
540 }
541 }
542
543 // check if we have a toplevel menu selected.
544 // If this is the case, we select another toplevel menu
545 if (len > 1 && path[0] instanceof JMenuBar) {
546 MenuElement currentMenu = path[1];
547 MenuElement nextMenu = findEnabledChild(
548 path[0].getSubElements(), currentMenu, direction);
549
550 if (nextMenu != null && nextMenu != currentMenu) {
551 MenuElement newSelection[];
552 if (len == 2) {
553 // menu is selected but its popup not shown
554 newSelection = new MenuElement[2];
555 newSelection[0] = path[0];
556 newSelection[1] = nextMenu;
557 } else {
558 // menu is selected and its popup is shown
559 newSelection = new MenuElement[3];
560 newSelection[0] = path[0];
561 newSelection[1] = nextMenu;
562 newSelection[2] = ((JMenu)nextMenu).getPopupMenu();
563 }
564 msm.setSelectedPath(newSelection);
565 }
566 }
567 }
568
569 private void selectItem(boolean direction) {
570 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
571 MenuElement path[] = msm.getSelectedPath();
572 if (path.length == 0) {
573 return;
574 }
575 int len = path.length;
576 if (len == 1 && path[0] instanceof JPopupMenu) {
577
578 JPopupMenu popup = (JPopupMenu) path[0];
579 MenuElement[] newPath = new MenuElement[2];
580 newPath[0] = popup;
581 newPath[1] = findEnabledChild(popup.getSubElements(), -1, direction);
582 msm.setSelectedPath(newPath);
583 } else if (len == 2 &&
584 path[0] instanceof JMenuBar && path[1] instanceof JMenu) {
585
586 // a toplevel menu is selected, but its popup not shown.
587 // Show the popup and select the first item
588 JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
589 MenuElement next =
590 findEnabledChild(popup.getSubElements(), -1, FORWARD);
591 MenuElement[] newPath;
621 // all items in the popup are disabled.
622 // We're going to find the parent popup menu and select
623 // its next item. If there's no parent popup menu (i.e.
624 // current menu is toplevel), do nothing
625 if (len > 2 && path[len-3] instanceof JPopupMenu) {
626 popup = ((JPopupMenu)path[len-3]);
627 next = findEnabledChild(popup.getSubElements(),
628 menu, direction);
629
630 if (next != null && next != menu) {
631 MenuElement[] newPath = new MenuElement[len-1];
632 System.arraycopy(path, 0, newPath, 0, len-2);
633 newPath[len-2] = next;
634 msm.setSelectedPath(newPath);
635 }
636 }
637 }
638
639 } else {
640 // just select the next item, no path expansion needed
641 MenuElement subs[] = path[len-2].getSubElements();
642 MenuElement nextChild =
643 findEnabledChild(subs, path[len-1], direction);
644 if (nextChild == null) {
645 nextChild = findEnabledChild(subs, -1, direction);
646 }
647 if (nextChild != null) {
648 path[len-1] = nextChild;
649 msm.setSelectedPath(path);
650 }
651 }
652 }
653
654 private void cancel() {
655 // 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's
656 // a protected method. The real solution could be to make
657 // firePopupMenuCanceled public and call it directly.
658 JPopupMenu lastPopup = getLastPopup();
659 if (lastPopup != null) {
660 lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
661 }
662 String mode = UIManager.getString("Menu.cancelMode");
663 if ("hideMenuTree".equals(mode)) {
664 MenuSelectionManager.defaultManager().clearSelectedPath();
665 } else {
666 shortenSelectedPath();
667 }
668 }
669
670 private void shortenSelectedPath() {
671 MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
672 if (path.length <= 2) {
673 MenuSelectionManager.defaultManager().clearSelectedPath();
674 return;
675 }
676 // unselect MenuItem and its Popup by default
677 int value = 2;
678 MenuElement lastElement = path[path.length - 1];
679 JPopupMenu lastPopup = getLastPopup();
680 if (lastElement == lastPopup) {
681 MenuElement previousElement = path[path.length - 2];
682 if (previousElement instanceof JMenu) {
683 JMenu lastMenu = (JMenu) previousElement;
684 if (lastMenu.isEnabled() && lastPopup.getComponentCount() > 0) {
685 // unselect the last visible popup only
686 value = 1;
687 } else {
688 // unselect invisible popup and two visible elements
689 value = 3;
690 }
691 }
692 }
693 if (path.length - value <= 2
694 && !UIManager.getBoolean("Menu.preserveTopLevelSelection")) {
695 // clear selection for the topLevelMenu
696 value = path.length;
697 }
698 MenuElement newPath[] = new MenuElement[path.length - value];
699 System.arraycopy(path, 0, newPath, 0, path.length - value);
700 MenuSelectionManager.defaultManager().setSelectedPath(newPath);
701 }
702 }
703
704 private static MenuElement nextEnabledChild(MenuElement e[],
705 int fromIndex, int toIndex) {
706 for (int i=fromIndex; i<=toIndex; i++) {
707 if (e[i] != null) {
708 Component comp = e[i].getComponent();
709 if ( comp != null
710 && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
711 && comp.isVisible()) {
712 return e[i];
713 }
714 }
715 }
716 return null;
717 }
718
719 private static MenuElement previousEnabledChild(MenuElement e[],
720 int fromIndex, int toIndex) {
721 for (int i=fromIndex; i>=toIndex; i--) {
722 if (e[i] != null) {
723 Component comp = e[i].getComponent();
724 if ( comp != null
725 && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
726 && comp.isVisible()) {
727 return e[i];
728 }
729 }
730 }
731 return null;
732 }
733
734 static MenuElement findEnabledChild(MenuElement e[], int fromIndex,
735 boolean forward) {
736 MenuElement result;
737 if (forward) {
738 result = nextEnabledChild(e, fromIndex+1, e.length-1);
739 if (result == null) result = nextEnabledChild(e, 0, fromIndex-1);
740 } else {
741 result = previousEnabledChild(e, fromIndex-1, 0);
742 if (result == null) result = previousEnabledChild(e, e.length-1,
743 fromIndex+1);
744 }
745 return result;
746 }
747
748 static MenuElement findEnabledChild(MenuElement e[],
749 MenuElement elem, boolean forward) {
750 for (int i=0; i<e.length; i++) {
751 if (e[i] == elem) {
752 return findEnabledChild(e, i, forward);
753 }
754 }
755 return null;
756 }
757
758 static class MouseGrabber implements ChangeListener,
759 AWTEventListener, ComponentListener, WindowListener {
760
761 Window grabbedWindow;
762 MenuElement[] lastPathSelected;
763
764 public MouseGrabber() {
765 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
766 msm.addChangeListener(this);
767 this.lastPathSelected = msm.getSelectedPath();
768 if(this.lastPathSelected.length != 0) {
|
1 /*
2 * Copyright (c) 1997, 2018, 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
310 /**
311 * Handles mnemonic for children JMenuItems.
312 * @since 1.5
313 */
314 private class BasicMenuKeyListener implements MenuKeyListener {
315 MenuElement menuToOpen = null;
316
317 public void menuKeyTyped(MenuKeyEvent e) {
318 if (menuToOpen != null) {
319 // we have a submenu to open
320 JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu();
321 MenuElement subitem = findEnabledChild(
322 subpopup.getSubElements(), -1, true);
323
324 ArrayList<MenuElement> lst = new ArrayList<MenuElement>(Arrays.asList(e.getPath()));
325 lst.add(menuToOpen);
326 lst.add(subpopup);
327 if (subitem != null) {
328 lst.add(subitem);
329 }
330 MenuElement[] newPath = new MenuElement[0];
331 newPath = lst.toArray(newPath);
332 MenuSelectionManager.defaultManager().setSelectedPath(newPath);
333 e.consume();
334 }
335 menuToOpen = null;
336 }
337
338 public void menuKeyPressed(MenuKeyEvent e) {
339 char keyChar = e.getKeyChar();
340
341 // Handle the case for Escape or Enter...
342 if (!Character.isLetterOrDigit(keyChar)) {
343 return;
344 }
345
346 MenuSelectionManager manager = e.getMenuSelectionManager();
347 MenuElement[] path = e.getPath();
348 MenuElement[] items = popupMenu.getSubElements();
349 int currentIndex = -1;
350 int matches = 0;
351 int firstMatch = -1;
352 int[] indexes = null;
353
354 for (int j = 0; j < items.length; j++) {
355 if (! (items[j] instanceof JMenuItem)) {
356 continue;
357 }
358 JMenuItem item = (JMenuItem)items[j];
359 int mnemonic = item.getMnemonic();
360 if (item.isEnabled() &&
361 item.isVisible() && lower(keyChar) == lower(mnemonic)) {
362 if (matches == 0) {
363 firstMatch = j;
364 matches++;
365 } else {
366 if (indexes == null) {
367 indexes = new int[items.length];
368 indexes[0] = firstMatch;
369 }
370 indexes[matches++] = j;
371 }
372 }
380 } else if (matches == 1) {
381 // Invoke the menu action
382 JMenuItem item = (JMenuItem)items[firstMatch];
383 if (item instanceof JMenu) {
384 // submenus are handled in menuKeyTyped
385 menuToOpen = item;
386 } else if (item.isEnabled()) {
387 // we have a menu item
388 manager.clearSelectedPath();
389 item.doClick();
390 }
391 e.consume();
392 } else {
393 // Select the menu item with the matching mnemonic. If
394 // the same mnemonic has been invoked then select the next
395 // menu item in the cycle.
396 MenuElement newItem;
397
398 newItem = items[indexes[(currentIndex + 1) % matches]];
399
400 MenuElement[] newPath = new MenuElement[path.length+1];
401 System.arraycopy(path, 0, newPath, 0, path.length);
402 newPath[path.length] = newItem;
403 manager.setSelectedPath(newPath);
404 e.consume();
405 }
406 }
407
408 public void menuKeyReleased(MenuKeyEvent e) {
409 }
410
411 private char lower(char keyChar) {
412 return Character.toLowerCase(keyChar);
413 }
414
415 private char lower(int mnemonic) {
416 return Character.toLowerCase((char) mnemonic);
417 }
418 }
419
420 private static class Actions extends UIAction {
453 else if (key == SELECT_PARENT) {
454 selectParentChild(PARENT);
455 }
456 else if (key == SELECT_CHILD) {
457 selectParentChild(CHILD);
458 }
459 else if (key == RETURN) {
460 doReturn();
461 }
462 }
463
464 private void doReturn() {
465 KeyboardFocusManager fmgr =
466 KeyboardFocusManager.getCurrentKeyboardFocusManager();
467 Component focusOwner = fmgr.getFocusOwner();
468 if(focusOwner != null && !(focusOwner instanceof JRootPane)) {
469 return;
470 }
471
472 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
473 MenuElement[] path = msm.getSelectedPath();
474 MenuElement lastElement;
475 if(path.length > 0) {
476 lastElement = path[path.length-1];
477 if(lastElement instanceof JMenu) {
478 MenuElement[] newPath = new MenuElement[path.length+1];
479 System.arraycopy(path,0,newPath,0,path.length);
480 newPath[path.length] = ((JMenu)lastElement).getPopupMenu();
481 msm.setSelectedPath(newPath);
482 } else if(lastElement instanceof JMenuItem) {
483 JMenuItem mi = (JMenuItem)lastElement;
484
485 if (mi.getUI() instanceof BasicMenuItemUI) {
486 ((BasicMenuItemUI)mi.getUI()).doClick(msm);
487 }
488 else {
489 msm.clearSelectedPath();
490 mi.doClick(0);
491 }
492 }
493 }
494 }
495 private void selectParentChild(boolean direction) {
496 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
497 MenuElement[] path = msm.getSelectedPath();
498 int len = path.length;
499
500 if (direction == PARENT) {
501 // selecting parent
502 int popupIndex = len-1;
503
504 if (len > 2 &&
505 // check if we have an open submenu. A submenu item may or
506 // may not be selected, so submenu popup can be either the
507 // last or next to the last item.
508 (path[popupIndex] instanceof JPopupMenu ||
509 path[--popupIndex] instanceof JPopupMenu) &&
510 !((JMenu)path[popupIndex-1]).isTopLevelMenu()) {
511
512 // we have a submenu, just close it
513 MenuElement[] newPath = new MenuElement[popupIndex];
514 System.arraycopy(path, 0, newPath, 0, popupIndex);
515 msm.setSelectedPath(newPath);
516 return;
517 }
518 } else {
519 // selecting child
520 if (len > 0 && path[len-1] instanceof JMenu &&
521 !((JMenu)path[len-1]).isTopLevelMenu()) {
522
523 // we have a submenu, open it
524 JMenu menu = (JMenu)path[len-1];
525 JPopupMenu popup = menu.getPopupMenu();
526 MenuElement[] subs = popup.getSubElements();
527 MenuElement item = findEnabledChild(subs, -1, true);
528 MenuElement[] newPath;
529
530 if (item == null) {
531 newPath = new MenuElement[len+1];
532 } else {
533 newPath = new MenuElement[len+2];
534 newPath[len+1] = item;
535 }
536 System.arraycopy(path, 0, newPath, 0, len);
537 newPath[len] = popup;
538 msm.setSelectedPath(newPath);
539 return;
540 }
541 }
542
543 // check if we have a toplevel menu selected.
544 // If this is the case, we select another toplevel menu
545 if (len > 1 && path[0] instanceof JMenuBar) {
546 MenuElement currentMenu = path[1];
547 MenuElement nextMenu = findEnabledChild(
548 path[0].getSubElements(), currentMenu, direction);
549
550 if (nextMenu != null && nextMenu != currentMenu) {
551 MenuElement[] newSelection;
552 if (len == 2) {
553 // menu is selected but its popup not shown
554 newSelection = new MenuElement[2];
555 newSelection[0] = path[0];
556 newSelection[1] = nextMenu;
557 } else {
558 // menu is selected and its popup is shown
559 newSelection = new MenuElement[3];
560 newSelection[0] = path[0];
561 newSelection[1] = nextMenu;
562 newSelection[2] = ((JMenu)nextMenu).getPopupMenu();
563 }
564 msm.setSelectedPath(newSelection);
565 }
566 }
567 }
568
569 private void selectItem(boolean direction) {
570 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
571 MenuElement[] path = msm.getSelectedPath();
572 if (path.length == 0) {
573 return;
574 }
575 int len = path.length;
576 if (len == 1 && path[0] instanceof JPopupMenu) {
577
578 JPopupMenu popup = (JPopupMenu) path[0];
579 MenuElement[] newPath = new MenuElement[2];
580 newPath[0] = popup;
581 newPath[1] = findEnabledChild(popup.getSubElements(), -1, direction);
582 msm.setSelectedPath(newPath);
583 } else if (len == 2 &&
584 path[0] instanceof JMenuBar && path[1] instanceof JMenu) {
585
586 // a toplevel menu is selected, but its popup not shown.
587 // Show the popup and select the first item
588 JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
589 MenuElement next =
590 findEnabledChild(popup.getSubElements(), -1, FORWARD);
591 MenuElement[] newPath;
621 // all items in the popup are disabled.
622 // We're going to find the parent popup menu and select
623 // its next item. If there's no parent popup menu (i.e.
624 // current menu is toplevel), do nothing
625 if (len > 2 && path[len-3] instanceof JPopupMenu) {
626 popup = ((JPopupMenu)path[len-3]);
627 next = findEnabledChild(popup.getSubElements(),
628 menu, direction);
629
630 if (next != null && next != menu) {
631 MenuElement[] newPath = new MenuElement[len-1];
632 System.arraycopy(path, 0, newPath, 0, len-2);
633 newPath[len-2] = next;
634 msm.setSelectedPath(newPath);
635 }
636 }
637 }
638
639 } else {
640 // just select the next item, no path expansion needed
641 MenuElement[] subs = path[len-2].getSubElements();
642 MenuElement nextChild =
643 findEnabledChild(subs, path[len-1], direction);
644 if (nextChild == null) {
645 nextChild = findEnabledChild(subs, -1, direction);
646 }
647 if (nextChild != null) {
648 path[len-1] = nextChild;
649 msm.setSelectedPath(path);
650 }
651 }
652 }
653
654 private void cancel() {
655 // 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's
656 // a protected method. The real solution could be to make
657 // firePopupMenuCanceled public and call it directly.
658 JPopupMenu lastPopup = getLastPopup();
659 if (lastPopup != null) {
660 lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE);
661 }
662 String mode = UIManager.getString("Menu.cancelMode");
663 if ("hideMenuTree".equals(mode)) {
664 MenuSelectionManager.defaultManager().clearSelectedPath();
665 } else {
666 shortenSelectedPath();
667 }
668 }
669
670 private void shortenSelectedPath() {
671 MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath();
672 if (path.length <= 2) {
673 MenuSelectionManager.defaultManager().clearSelectedPath();
674 return;
675 }
676 // unselect MenuItem and its Popup by default
677 int value = 2;
678 MenuElement lastElement = path[path.length - 1];
679 JPopupMenu lastPopup = getLastPopup();
680 if (lastElement == lastPopup) {
681 MenuElement previousElement = path[path.length - 2];
682 if (previousElement instanceof JMenu) {
683 JMenu lastMenu = (JMenu) previousElement;
684 if (lastMenu.isEnabled() && lastPopup.getComponentCount() > 0) {
685 // unselect the last visible popup only
686 value = 1;
687 } else {
688 // unselect invisible popup and two visible elements
689 value = 3;
690 }
691 }
692 }
693 if (path.length - value <= 2
694 && !UIManager.getBoolean("Menu.preserveTopLevelSelection")) {
695 // clear selection for the topLevelMenu
696 value = path.length;
697 }
698 MenuElement[] newPath = new MenuElement[path.length - value];
699 System.arraycopy(path, 0, newPath, 0, path.length - value);
700 MenuSelectionManager.defaultManager().setSelectedPath(newPath);
701 }
702 }
703
704 private static MenuElement nextEnabledChild(MenuElement[] e,
705 int fromIndex, int toIndex) {
706 for (int i=fromIndex; i<=toIndex; i++) {
707 if (e[i] != null) {
708 Component comp = e[i].getComponent();
709 if ( comp != null
710 && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
711 && comp.isVisible()) {
712 return e[i];
713 }
714 }
715 }
716 return null;
717 }
718
719 private static MenuElement previousEnabledChild(MenuElement[] e,
720 int fromIndex, int toIndex) {
721 for (int i=fromIndex; i>=toIndex; i--) {
722 if (e[i] != null) {
723 Component comp = e[i].getComponent();
724 if ( comp != null
725 && (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable"))
726 && comp.isVisible()) {
727 return e[i];
728 }
729 }
730 }
731 return null;
732 }
733
734 static MenuElement findEnabledChild(MenuElement[] e, int fromIndex,
735 boolean forward) {
736 MenuElement result;
737 if (forward) {
738 result = nextEnabledChild(e, fromIndex+1, e.length-1);
739 if (result == null) result = nextEnabledChild(e, 0, fromIndex-1);
740 } else {
741 result = previousEnabledChild(e, fromIndex-1, 0);
742 if (result == null) result = previousEnabledChild(e, e.length-1,
743 fromIndex+1);
744 }
745 return result;
746 }
747
748 static MenuElement findEnabledChild(MenuElement[] e,
749 MenuElement elem, boolean forward) {
750 for (int i=0; i<e.length; i++) {
751 if (e[i] == elem) {
752 return findEnabledChild(e, i, forward);
753 }
754 }
755 return null;
756 }
757
758 static class MouseGrabber implements ChangeListener,
759 AWTEventListener, ComponentListener, WindowListener {
760
761 Window grabbedWindow;
762 MenuElement[] lastPathSelected;
763
764 public MouseGrabber() {
765 MenuSelectionManager msm = MenuSelectionManager.defaultManager();
766 msm.addChangeListener(this);
767 this.lastPathSelected = msm.getSelectedPath();
768 if(this.lastPathSelected.length != 0) {
|