/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.awt;
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.MenuSelectionManager;
import javax.swing.MenuElement;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicPopupMenuUI;
/**
* Controlls keys for PopupMenu - UP, DOWN, LEFT, RIGHT, ESCAPE, RETURN
*/
final class NbPopupMenuUI extends BasicPopupMenuUI {
protected void installKeyboardActions() {
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
popupMenu.registerKeyboardAction(new CancelAction(), ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
popupMenu.registerKeyboardAction(new SelectNextItemAction(), ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
popupMenu.registerKeyboardAction(new SelectPreviousItemAction(), ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
popupMenu.registerKeyboardAction(new SelectChildItemAction(), ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
popupMenu.registerKeyboardAction(new SelectParentItemAction(), ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
Action retAction = new ReturnAction();
ks = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
popupMenu.registerKeyboardAction(retAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
// #16189. Also for space the same action.
ks = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
popupMenu.registerKeyboardAction(retAction, ks, JComponent.WHEN_IN_FOCUSED_WINDOW);
}
protected void uninstallKeyboardActions() {
KeyStroke ks = KeyStroke.getKeyStroke((char) KeyEvent.VK_ESCAPE);
popupMenu.unregisterKeyboardAction(ks);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
popupMenu.unregisterKeyboardAction(ks);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
popupMenu.unregisterKeyboardAction(ks);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
popupMenu.unregisterKeyboardAction(ks);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
popupMenu.unregisterKeyboardAction(ks);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
popupMenu.unregisterKeyboardAction(ks);
ks = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0);
popupMenu.unregisterKeyboardAction(ks);
}
final static class CancelAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
if (path[path.length - 1] instanceof JPopupMenu) {
MenuElement newPath[] = new MenuElement[path.length - 1];
System.arraycopy(path, 0, newPath, 0, path.length - 1);
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
} else if (path.length > 2) {
MenuElement newPath[] = new MenuElement[path.length - 2];
System.arraycopy(path, 0, newPath, 0, path.length - 2);
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
} else {
MenuSelectionManager.defaultManager().clearSelectedPath();
}
}
}
final static class ReturnAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
MenuElement lastElement;
if (path.length > 0) {
lastElement = path[path.length - 1];
if ((lastElement instanceof JMenuItem) &&
(!(lastElement instanceof JMenu)
// XXX #11048: The Actions.SubMenu acts as pure JMenuItem
// for cases it has one (or zero) sub-item.
|| (lastElement instanceof Actions.SubMenu
&& ((Actions.SubMenu)lastElement).getMenuComponentCount() <= 1))) {
MenuSelectionManager.defaultManager().clearSelectedPath();
((JMenuItem) lastElement).doClick(0);
((JMenuItem) lastElement).setArmed(false);
}
}
}
}
static MenuElement nextEnabledChild(MenuElement e[], int fromIndex) {
int i, c;
for(i = fromIndex, c = e.length; i < c; i++) {
if (e[i] != null) {
Component comp = e[i].getComponent();
if(comp != null && comp.isEnabled()) {
return e[i];
}
}
}
return null;
}
static MenuElement previousEnabledChild(MenuElement e[], int fromIndex) {
int i;
for(i = fromIndex; i >= 0; i--) {
if (e[i] != null) {
Component comp = e[i].getComponent();
if(comp != null && comp.isEnabled()) {
return e[i];
}
}
}
return null;
}
final static class SelectNextItemAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
MenuElement currentSelection[] = MenuSelectionManager.defaultManager().getSelectedPath();
if(currentSelection.length > 1) {
boolean visiblePopup = (currentSelection[currentSelection.length - 1] instanceof JPopupMenu);
int parentIdx = (visiblePopup ? 3 : 2);
int childIdx = (visiblePopup ? 2 : 1);
MenuElement parent = currentSelection[currentSelection.length - parentIdx];
MenuElement childs[] = parent.getSubElements();
MenuElement nextChild;
int i, c;
for(i = 0, c = childs.length; i < c; i++) {
if(childs[i] == currentSelection[currentSelection.length - childIdx]) {
nextChild = nextEnabledChild(childs, i + 1);
if(nextChild == null)
nextChild = nextEnabledChild(childs, 0);
if(nextChild != null) {
JMenu childMenu = null;
if (nextChild instanceof JMenu
// XXX 11048. See first XXX 11048 comment.
&& (!(nextChild instanceof Actions.SubMenu)
|| ((Actions.SubMenu)nextChild).getMenuComponentCount() > 1)) {
childMenu = (JMenu) nextChild;
}
if (visiblePopup != (childMenu != null)) {
if (visiblePopup) {
MenuElement[] newSelection = new MenuElement[currentSelection.length - 1];
System.arraycopy(currentSelection, 0, newSelection, 0, newSelection.length - 1);
newSelection[newSelection.length - 1] = nextChild;
currentSelection = newSelection;
} else {
MenuElement[] newSelection = new MenuElement[currentSelection.length + 1];
System.arraycopy(currentSelection, 0, newSelection, 0, currentSelection.length);
newSelection[newSelection.length - 2] = childMenu;
JPopupMenu tmpPopup = childMenu.getPopupMenu();
newSelection[newSelection.length - 1] = tmpPopup;
changeTargetUI(tmpPopup);
currentSelection = newSelection;
}
} else if (visiblePopup) {
currentSelection[currentSelection.length - 2] = nextChild;
JPopupMenu tmpPopup = childMenu.getPopupMenu();
currentSelection[currentSelection.length - 1] = tmpPopup;
changeTargetUI(tmpPopup);
} else {
currentSelection[currentSelection.length - 1] = nextChild;
}
MenuSelectionManager.defaultManager().setSelectedPath(currentSelection);
}
break;
}
}
}
}
}
final static class SelectPreviousItemAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
MenuElement currentSelection[] = MenuSelectionManager.defaultManager().getSelectedPath();
if (currentSelection.length > 1) {
boolean visiblePopup = (currentSelection[currentSelection.length - 1] instanceof JPopupMenu);
int parentIdx = (visiblePopup ? 3 : 2);
int childIdx = (visiblePopup ? 2 : 1);
MenuElement parent = currentSelection[currentSelection.length - parentIdx];
MenuElement childs[] = parent.getSubElements();
MenuElement nextChild;
int i, c;
for (i = 0, c = childs.length; i < c; i++) {
if (childs[i] == currentSelection[currentSelection.length - childIdx]) {
nextChild = previousEnabledChild(childs, i - 1);
if (nextChild == null) {
nextChild = previousEnabledChild(childs, childs.length - 1);
}
if (nextChild != null) {
JMenu childMenu = null;
if (nextChild instanceof JMenu
// XXX 11048. See first XXX 11048 comment.
&& (!(nextChild instanceof Actions.SubMenu)
|| ((Actions.SubMenu)nextChild).getMenuComponentCount() > 1)) {
childMenu = (JMenu) nextChild;
}
if (visiblePopup != (childMenu != null)) {
if (visiblePopup) {
MenuElement[] newSelection = new MenuElement[currentSelection.length - 1];
System.arraycopy(currentSelection, 0, newSelection, 0, newSelection.length - 1);
newSelection[newSelection.length - 1] = nextChild;
currentSelection = newSelection;
} else {
MenuElement[] newSelection = new MenuElement[currentSelection.length + 1];
System.arraycopy(currentSelection, 0, newSelection, 0, currentSelection.length);
newSelection[newSelection.length - 2] = childMenu;
JPopupMenu tmpPopup = childMenu.getPopupMenu();
newSelection[newSelection.length - 1] = tmpPopup;
changeTargetUI(tmpPopup);
currentSelection = newSelection;
}
} else if (visiblePopup) {
currentSelection[currentSelection.length - 2] = nextChild;
JPopupMenu tmpPopup = childMenu.getPopupMenu();
currentSelection[currentSelection.length - 1] = tmpPopup;
changeTargetUI(tmpPopup);
} else {
currentSelection[currentSelection.length - 1] = nextChild;
}
MenuSelectionManager.defaultManager().setSelectedPath(currentSelection);
}
break;
}
}
}
}
}
final static class SelectChildItemAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
if (path.length > 0) {
Component compo = path[path.length - 1].getComponent();
boolean visiblePopup = false;
if (compo instanceof JPopupMenu) {
if (path.length == 1) {
return;
}
compo = path[path.length - 2].getComponent();
visiblePopup = true;
}
if (compo.isEnabled() &&
(compo instanceof JMenu) &&
// XXX 11048. See first XXX 11048 comment.
(!(compo instanceof Actions.SubMenu)
|| ((Actions.SubMenu)compo).getMenuComponentCount() > 1) &&
!((JMenu) compo).isTopLevelMenu()) {
JPopupMenu popup = ((JMenu) compo).getPopupMenu();
changeTargetUI(popup);
MenuElement subElements[] = popup.getSubElements();
if (subElements.length > 0) {
int adder = (visiblePopup ? 1 : 2);
int relativePopupIdx = (visiblePopup ? 1 : 0);
MenuElement enabledChild = nextEnabledChild(subElements, 0);
if (enabledChild != null) {
//boolean setPopup = (enabledChild.getComponent() instanceof JMenu);
java.awt.Component c = enabledChild.getComponent();
boolean setPopup = (c instanceof JMenu
// XXX 11048. See first XXX 11048 comment.
&& (!(c instanceof Actions.SubMenu)
|| ((Actions.SubMenu)c).getMenuComponentCount() > 1));
if (setPopup) {
adder++;
}
MenuElement newPath[] = new MenuElement[path.length + adder];
System.arraycopy(path, 0, newPath, 0, path.length);
newPath[path.length - relativePopupIdx] = popup;
newPath[path.length + 1 - relativePopupIdx] = enabledChild;
if (setPopup) {
JMenu jmenu = (JMenu) enabledChild.getComponent();
newPath[newPath.length - 1] = jmenu.getPopupMenu();
}
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
}
}
}
}
}
}
final static class SelectParentItemAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath();
if (path.length > 3) {
if (path[path.length - 1].getComponent() instanceof JPopupMenu) {
MenuElement[] newPath = new MenuElement[path.length - 1];
System.arraycopy(path, 0, newPath, 0, newPath.length);
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
} else {
MenuElement[] newPath = new MenuElement[path.length - 2];
System.arraycopy(path, 0, newPath, 0, newPath.length);
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
}
} else if (path.length > 2) {
if (path[path.length - 1].getComponent() instanceof JPopupMenu) {
MenuElement[] newPath = new MenuElement[path.length - 1];
System.arraycopy(path, 0, newPath, 0, newPath.length);
MenuSelectionManager.defaultManager().setSelectedPath(newPath);
}
}
}
}
static void changeTargetUI(JPopupMenu menu) {
if (menu.getUI() instanceof NbPopupMenuUI) {
return;
}
menu.setUI(new NbPopupMenuUI());
}
}