|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package javax.swing.plaf.basic; |
|
|
|
import javax.swing.*; |
|
import javax.swing.event.*; |
|
import javax.swing.plaf.*; |
|
import javax.swing.plaf.basic.*; |
|
import javax.swing.border.*; |
|
|
|
import java.applet.Applet; |
|
|
|
import java.awt.Component; |
|
import java.awt.Container; |
|
import java.awt.Dimension; |
|
import java.awt.KeyboardFocusManager; |
|
import java.awt.Window; |
|
import java.awt.event.*; |
|
import java.awt.AWTEvent; |
|
import java.awt.Toolkit; |
|
|
|
import java.beans.PropertyChangeListener; |
|
import java.beans.PropertyChangeEvent; |
|
|
|
import java.util.*; |
|
|
|
import sun.swing.DefaultLookup; |
|
import sun.swing.UIAction; |
|
|
|
import sun.awt.AppContext; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class BasicPopupMenuUI extends PopupMenuUI { |
|
static final StringBuilder MOUSE_GRABBER_KEY = new StringBuilder( |
|
"javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber"); |
|
static final StringBuilder MENU_KEYBOARD_HELPER_KEY = new StringBuilder( |
|
"javax.swing.plaf.basic.BasicPopupMenuUI.MenuKeyboardHelper"); |
|
|
|
protected JPopupMenu popupMenu = null; |
|
private transient PopupMenuListener popupMenuListener = null; |
|
private MenuKeyListener menuKeyListener = null; |
|
|
|
private static boolean checkedUnpostPopup; |
|
private static boolean unpostPopup; |
|
|
|
public static ComponentUI createUI(JComponent x) { |
|
return new BasicPopupMenuUI(); |
|
} |
|
|
|
public BasicPopupMenuUI() { |
|
BasicLookAndFeel.needsEventHelper = true; |
|
LookAndFeel laf = UIManager.getLookAndFeel(); |
|
if (laf instanceof BasicLookAndFeel) { |
|
((BasicLookAndFeel)laf).installAWTEventListener(); |
|
} |
|
} |
|
|
|
public void installUI(JComponent c) { |
|
popupMenu = (JPopupMenu) c; |
|
|
|
installDefaults(); |
|
installListeners(); |
|
installKeyboardActions(); |
|
} |
|
|
|
public void installDefaults() { |
|
if (popupMenu.getLayout() == null || |
|
popupMenu.getLayout() instanceof UIResource) |
|
popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS)); |
|
|
|
LookAndFeel.installProperty(popupMenu, "opaque", Boolean.TRUE); |
|
LookAndFeel.installBorder(popupMenu, "PopupMenu.border"); |
|
LookAndFeel.installColorsAndFont(popupMenu, |
|
"PopupMenu.background", |
|
"PopupMenu.foreground", |
|
"PopupMenu.font"); |
|
} |
|
|
|
protected void installListeners() { |
|
if (popupMenuListener == null) { |
|
popupMenuListener = new BasicPopupMenuListener(); |
|
} |
|
popupMenu.addPopupMenuListener(popupMenuListener); |
|
|
|
if (menuKeyListener == null) { |
|
menuKeyListener = new BasicMenuKeyListener(); |
|
} |
|
popupMenu.addMenuKeyListener(menuKeyListener); |
|
|
|
AppContext context = AppContext.getAppContext(); |
|
synchronized (MOUSE_GRABBER_KEY) { |
|
MouseGrabber mouseGrabber = (MouseGrabber)context.get( |
|
MOUSE_GRABBER_KEY); |
|
if (mouseGrabber == null) { |
|
mouseGrabber = new MouseGrabber(); |
|
context.put(MOUSE_GRABBER_KEY, mouseGrabber); |
|
} |
|
} |
|
synchronized (MENU_KEYBOARD_HELPER_KEY) { |
|
MenuKeyboardHelper helper = |
|
(MenuKeyboardHelper)context.get(MENU_KEYBOARD_HELPER_KEY); |
|
if (helper == null) { |
|
helper = new MenuKeyboardHelper(); |
|
context.put(MENU_KEYBOARD_HELPER_KEY, helper); |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
msm.addChangeListener(helper); |
|
} |
|
} |
|
} |
|
|
|
protected void installKeyboardActions() { |
|
} |
|
|
|
static InputMap getInputMap(JPopupMenu popup, JComponent c) { |
|
InputMap windowInputMap = null; |
|
Object[] bindings = (Object[])UIManager.get("PopupMenu.selectedWindowInputMapBindings"); |
|
if (bindings != null) { |
|
windowInputMap = LookAndFeel.makeComponentInputMap(c, bindings); |
|
if (!popup.getComponentOrientation().isLeftToRight()) { |
|
Object[] km = (Object[])UIManager.get("PopupMenu.selectedWindowInputMapBindings.RightToLeft"); |
|
if (km != null) { |
|
InputMap rightToLeftInputMap = LookAndFeel.makeComponentInputMap(c, km); |
|
rightToLeftInputMap.setParent(windowInputMap); |
|
windowInputMap = rightToLeftInputMap; |
|
} |
|
} |
|
} |
|
return windowInputMap; |
|
} |
|
|
|
static ActionMap getActionMap() { |
|
return LazyActionMap.getActionMap(BasicPopupMenuUI.class, |
|
"PopupMenu.actionMap"); |
|
} |
|
|
|
static void loadActionMap(LazyActionMap map) { |
|
map.put(new Actions(Actions.CANCEL)); |
|
map.put(new Actions(Actions.SELECT_NEXT)); |
|
map.put(new Actions(Actions.SELECT_PREVIOUS)); |
|
map.put(new Actions(Actions.SELECT_PARENT)); |
|
map.put(new Actions(Actions.SELECT_CHILD)); |
|
map.put(new Actions(Actions.RETURN)); |
|
BasicLookAndFeel.installAudioActionMap(map); |
|
} |
|
|
|
public void uninstallUI(JComponent c) { |
|
uninstallDefaults(); |
|
uninstallListeners(); |
|
uninstallKeyboardActions(); |
|
|
|
popupMenu = null; |
|
} |
|
|
|
protected void uninstallDefaults() { |
|
LookAndFeel.uninstallBorder(popupMenu); |
|
} |
|
|
|
protected void uninstallListeners() { |
|
if (popupMenuListener != null) { |
|
popupMenu.removePopupMenuListener(popupMenuListener); |
|
} |
|
if (menuKeyListener != null) { |
|
popupMenu.removeMenuKeyListener(menuKeyListener); |
|
} |
|
} |
|
|
|
protected void uninstallKeyboardActions() { |
|
SwingUtilities.replaceUIActionMap(popupMenu, null); |
|
SwingUtilities.replaceUIInputMap(popupMenu, |
|
JComponent.WHEN_IN_FOCUSED_WINDOW, null); |
|
} |
|
|
|
static MenuElement getFirstPopup() { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement[] p = msm.getSelectedPath(); |
|
MenuElement me = null; |
|
|
|
for(int i = 0 ; me == null && i < p.length ; i++) { |
|
if (p[i] instanceof JPopupMenu) |
|
me = p[i]; |
|
} |
|
|
|
return me; |
|
} |
|
|
|
static JPopupMenu getLastPopup() { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement[] p = msm.getSelectedPath(); |
|
JPopupMenu popup = null; |
|
|
|
for(int i = p.length - 1; popup == null && i >= 0; i--) { |
|
if (p[i] instanceof JPopupMenu) |
|
popup = (JPopupMenu)p[i]; |
|
} |
|
return popup; |
|
} |
|
|
|
static List<JPopupMenu> getPopups() { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement[] p = msm.getSelectedPath(); |
|
|
|
List<JPopupMenu> list = new ArrayList<JPopupMenu>(p.length); |
|
for (MenuElement element : p) { |
|
if (element instanceof JPopupMenu) { |
|
list.add((JPopupMenu) element); |
|
} |
|
} |
|
return list; |
|
} |
|
|
|
public boolean isPopupTrigger(MouseEvent e) { |
|
return ((e.getID()==MouseEvent.MOUSE_RELEASED) |
|
&& ((e.getModifiers() & MouseEvent.BUTTON3_MASK)!=0)); |
|
} |
|
|
|
private static boolean checkInvokerEqual(MenuElement present, MenuElement last) { |
|
Component invokerPresent = present.getComponent(); |
|
Component invokerLast = last.getComponent(); |
|
|
|
if (invokerPresent instanceof JPopupMenu) { |
|
invokerPresent = ((JPopupMenu)invokerPresent).getInvoker(); |
|
} |
|
if (invokerLast instanceof JPopupMenu) { |
|
invokerLast = ((JPopupMenu)invokerLast).getInvoker(); |
|
} |
|
return (invokerPresent == invokerLast); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private class BasicPopupMenuListener implements PopupMenuListener { |
|
public void popupMenuCanceled(PopupMenuEvent e) { |
|
} |
|
|
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
|
} |
|
|
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
|
BasicLookAndFeel.playSound((JPopupMenu)e.getSource(), |
|
"PopupMenu.popupSound"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private class BasicMenuKeyListener implements MenuKeyListener { |
|
MenuElement menuToOpen = null; |
|
|
|
public void menuKeyTyped(MenuKeyEvent e) { |
|
if (menuToOpen != null) { |
|
|
|
JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu(); |
|
MenuElement subitem = findEnabledChild( |
|
subpopup.getSubElements(), -1, true); |
|
|
|
ArrayList<MenuElement> lst = new ArrayList<MenuElement>(Arrays.asList(e.getPath())); |
|
lst.add(menuToOpen); |
|
lst.add(subpopup); |
|
if (subitem != null) { |
|
lst.add(subitem); |
|
} |
|
MenuElement newPath[] = new MenuElement[0]; |
|
newPath = lst.toArray(newPath); |
|
MenuSelectionManager.defaultManager().setSelectedPath(newPath); |
|
e.consume(); |
|
} |
|
menuToOpen = null; |
|
} |
|
|
|
public void menuKeyPressed(MenuKeyEvent e) { |
|
char keyChar = e.getKeyChar(); |
|
|
|
|
|
if (!Character.isLetterOrDigit(keyChar)) { |
|
return; |
|
} |
|
|
|
MenuSelectionManager manager = e.getMenuSelectionManager(); |
|
MenuElement path[] = e.getPath(); |
|
MenuElement items[] = popupMenu.getSubElements(); |
|
int currentIndex = -1; |
|
int matches = 0; |
|
int firstMatch = -1; |
|
int indexes[] = null; |
|
|
|
for (int j = 0; j < items.length; j++) { |
|
if (! (items[j] instanceof JMenuItem)) { |
|
continue; |
|
} |
|
JMenuItem item = (JMenuItem)items[j]; |
|
int mnemonic = item.getMnemonic(); |
|
if (item.isEnabled() && |
|
item.isVisible() && lower(keyChar) == lower(mnemonic)) { |
|
if (matches == 0) { |
|
firstMatch = j; |
|
matches++; |
|
} else { |
|
if (indexes == null) { |
|
indexes = new int[items.length]; |
|
indexes[0] = firstMatch; |
|
} |
|
indexes[matches++] = j; |
|
} |
|
} |
|
if (item.isArmed() || item.isSelected()) { |
|
currentIndex = matches - 1; |
|
} |
|
} |
|
|
|
if (matches == 0) { |
|
// no op |
|
} else if (matches == 1) { |
|
|
|
JMenuItem item = (JMenuItem)items[firstMatch]; |
|
if (item instanceof JMenu) { |
|
|
|
menuToOpen = item; |
|
} else if (item.isEnabled()) { |
|
|
|
manager.clearSelectedPath(); |
|
item.doClick(); |
|
} |
|
e.consume(); |
|
} else { |
|
// Select the menu item with the matching mnemonic. If |
|
// the same mnemonic has been invoked then select the next |
|
|
|
MenuElement newItem; |
|
|
|
newItem = items[indexes[(currentIndex + 1) % matches]]; |
|
|
|
MenuElement newPath[] = new MenuElement[path.length+1]; |
|
System.arraycopy(path, 0, newPath, 0, path.length); |
|
newPath[path.length] = newItem; |
|
manager.setSelectedPath(newPath); |
|
e.consume(); |
|
} |
|
} |
|
|
|
public void menuKeyReleased(MenuKeyEvent e) { |
|
} |
|
|
|
private char lower(char keyChar) { |
|
return Character.toLowerCase(keyChar); |
|
} |
|
|
|
private char lower(int mnemonic) { |
|
return Character.toLowerCase((char) mnemonic); |
|
} |
|
} |
|
|
|
private static class Actions extends UIAction { |
|
|
|
private static final String CANCEL = "cancel"; |
|
private static final String SELECT_NEXT = "selectNext"; |
|
private static final String SELECT_PREVIOUS = "selectPrevious"; |
|
private static final String SELECT_PARENT = "selectParent"; |
|
private static final String SELECT_CHILD = "selectChild"; |
|
private static final String RETURN = "return"; |
|
|
|
|
|
private static final boolean FORWARD = true; |
|
private static final boolean BACKWARD = false; |
|
|
|
|
|
private static final boolean PARENT = false; |
|
private static final boolean CHILD = true; |
|
|
|
|
|
Actions(String key) { |
|
super(key); |
|
} |
|
|
|
public void actionPerformed(ActionEvent e) { |
|
String key = getName(); |
|
if (key == CANCEL) { |
|
cancel(); |
|
} |
|
else if (key == SELECT_NEXT) { |
|
selectItem(FORWARD); |
|
} |
|
else if (key == SELECT_PREVIOUS) { |
|
selectItem(BACKWARD); |
|
} |
|
else if (key == SELECT_PARENT) { |
|
selectParentChild(PARENT); |
|
} |
|
else if (key == SELECT_CHILD) { |
|
selectParentChild(CHILD); |
|
} |
|
else if (key == RETURN) { |
|
doReturn(); |
|
} |
|
} |
|
|
|
private void doReturn() { |
|
KeyboardFocusManager fmgr = |
|
KeyboardFocusManager.getCurrentKeyboardFocusManager(); |
|
Component focusOwner = fmgr.getFocusOwner(); |
|
if(focusOwner != null && !(focusOwner instanceof JRootPane)) { |
|
return; |
|
} |
|
|
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement path[] = msm.getSelectedPath(); |
|
MenuElement lastElement; |
|
if(path.length > 0) { |
|
lastElement = path[path.length-1]; |
|
if(lastElement instanceof JMenu) { |
|
MenuElement newPath[] = new MenuElement[path.length+1]; |
|
System.arraycopy(path,0,newPath,0,path.length); |
|
newPath[path.length] = ((JMenu)lastElement).getPopupMenu(); |
|
msm.setSelectedPath(newPath); |
|
} else if(lastElement instanceof JMenuItem) { |
|
JMenuItem mi = (JMenuItem)lastElement; |
|
|
|
if (mi.getUI() instanceof BasicMenuItemUI) { |
|
((BasicMenuItemUI)mi.getUI()).doClick(msm); |
|
} |
|
else { |
|
msm.clearSelectedPath(); |
|
mi.doClick(0); |
|
} |
|
} |
|
} |
|
} |
|
private void selectParentChild(boolean direction) { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement path[] = msm.getSelectedPath(); |
|
int len = path.length; |
|
|
|
if (direction == PARENT) { |
|
|
|
int popupIndex = len-1; |
|
|
|
if (len > 2 && |
|
// check if we have an open submenu. A submenu item may or |
|
// may not be selected, so submenu popup can be either the |
|
|
|
(path[popupIndex] instanceof JPopupMenu || |
|
path[--popupIndex] instanceof JPopupMenu) && |
|
!((JMenu)path[popupIndex-1]).isTopLevelMenu()) { |
|
|
|
|
|
MenuElement newPath[] = new MenuElement[popupIndex]; |
|
System.arraycopy(path, 0, newPath, 0, popupIndex); |
|
msm.setSelectedPath(newPath); |
|
return; |
|
} |
|
} else { |
|
|
|
if (len > 0 && path[len-1] instanceof JMenu && |
|
!((JMenu)path[len-1]).isTopLevelMenu()) { |
|
|
|
|
|
JMenu menu = (JMenu)path[len-1]; |
|
JPopupMenu popup = menu.getPopupMenu(); |
|
MenuElement[] subs = popup.getSubElements(); |
|
MenuElement item = findEnabledChild(subs, -1, true); |
|
MenuElement[] newPath; |
|
|
|
if (item == null) { |
|
newPath = new MenuElement[len+1]; |
|
} else { |
|
newPath = new MenuElement[len+2]; |
|
newPath[len+1] = item; |
|
} |
|
System.arraycopy(path, 0, newPath, 0, len); |
|
newPath[len] = popup; |
|
msm.setSelectedPath(newPath); |
|
return; |
|
} |
|
} |
|
|
|
// check if we have a toplevel menu selected. |
|
|
|
if (len > 1 && path[0] instanceof JMenuBar) { |
|
MenuElement currentMenu = path[1]; |
|
MenuElement nextMenu = findEnabledChild( |
|
path[0].getSubElements(), currentMenu, direction); |
|
|
|
if (nextMenu != null && nextMenu != currentMenu) { |
|
MenuElement newSelection[]; |
|
if (len == 2) { |
|
|
|
newSelection = new MenuElement[2]; |
|
newSelection[0] = path[0]; |
|
newSelection[1] = nextMenu; |
|
} else { |
|
|
|
newSelection = new MenuElement[3]; |
|
newSelection[0] = path[0]; |
|
newSelection[1] = nextMenu; |
|
newSelection[2] = ((JMenu)nextMenu).getPopupMenu(); |
|
} |
|
msm.setSelectedPath(newSelection); |
|
} |
|
} |
|
} |
|
|
|
private void selectItem(boolean direction) { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement path[] = msm.getSelectedPath(); |
|
if (path.length == 0) { |
|
return; |
|
} |
|
int len = path.length; |
|
if (len == 1 && path[0] instanceof JPopupMenu) { |
|
|
|
JPopupMenu popup = (JPopupMenu) path[0]; |
|
MenuElement[] newPath = new MenuElement[2]; |
|
newPath[0] = popup; |
|
newPath[1] = findEnabledChild(popup.getSubElements(), -1, direction); |
|
msm.setSelectedPath(newPath); |
|
} else if (len == 2 && |
|
path[0] instanceof JMenuBar && path[1] instanceof JMenu) { |
|
|
|
// a toplevel menu is selected, but its popup not shown. |
|
|
|
JPopupMenu popup = ((JMenu)path[1]).getPopupMenu(); |
|
MenuElement next = |
|
findEnabledChild(popup.getSubElements(), -1, FORWARD); |
|
MenuElement[] newPath; |
|
|
|
if (next != null) { |
|
|
|
newPath = new MenuElement[4]; |
|
newPath[3] = next; |
|
} else { |
|
|
|
newPath = new MenuElement[3]; |
|
} |
|
System.arraycopy(path, 0, newPath, 0, 2); |
|
newPath[2] = popup; |
|
msm.setSelectedPath(newPath); |
|
|
|
} else if (path[len-1] instanceof JPopupMenu && |
|
path[len-2] instanceof JMenu) { |
|
|
|
// a menu (not necessarily toplevel) is open and its popup |
|
|
|
JMenu menu = (JMenu)path[len-2]; |
|
JPopupMenu popup = menu.getPopupMenu(); |
|
MenuElement next = |
|
findEnabledChild(popup.getSubElements(), -1, direction); |
|
|
|
if (next != null) { |
|
MenuElement[] newPath = new MenuElement[len+1]; |
|
System.arraycopy(path, 0, newPath, 0, len); |
|
newPath[len] = next; |
|
msm.setSelectedPath(newPath); |
|
} else { |
|
// all items in the popup are disabled. |
|
// We're going to find the parent popup menu and select |
|
// its next item. If there's no parent popup menu (i.e. |
|
|
|
if (len > 2 && path[len-3] instanceof JPopupMenu) { |
|
popup = ((JPopupMenu)path[len-3]); |
|
next = findEnabledChild(popup.getSubElements(), |
|
menu, direction); |
|
|
|
if (next != null && next != menu) { |
|
MenuElement[] newPath = new MenuElement[len-1]; |
|
System.arraycopy(path, 0, newPath, 0, len-2); |
|
newPath[len-2] = next; |
|
msm.setSelectedPath(newPath); |
|
} |
|
} |
|
} |
|
|
|
} else { |
|
|
|
MenuElement subs[] = path[len-2].getSubElements(); |
|
MenuElement nextChild = |
|
findEnabledChild(subs, path[len-1], direction); |
|
if (nextChild == null) { |
|
nextChild = findEnabledChild(subs, -1, direction); |
|
} |
|
if (nextChild != null) { |
|
path[len-1] = nextChild; |
|
msm.setSelectedPath(path); |
|
} |
|
} |
|
} |
|
|
|
private void cancel() { |
|
// 4234793: This action should call JPopupMenu.firePopupMenuCanceled but it's |
|
// a protected method. The real solution could be to make |
|
|
|
JPopupMenu lastPopup = getLastPopup(); |
|
if (lastPopup != null) { |
|
lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE); |
|
} |
|
String mode = UIManager.getString("Menu.cancelMode"); |
|
if ("hideMenuTree".equals(mode)) { |
|
MenuSelectionManager.defaultManager().clearSelectedPath(); |
|
} else { |
|
shortenSelectedPath(); |
|
} |
|
} |
|
|
|
private void shortenSelectedPath() { |
|
MenuElement path[] = MenuSelectionManager.defaultManager().getSelectedPath(); |
|
if (path.length <= 2) { |
|
MenuSelectionManager.defaultManager().clearSelectedPath(); |
|
return; |
|
} |
|
|
|
int value = 2; |
|
MenuElement lastElement = path[path.length - 1]; |
|
JPopupMenu lastPopup = getLastPopup(); |
|
if (lastElement == lastPopup) { |
|
MenuElement previousElement = path[path.length - 2]; |
|
if (previousElement instanceof JMenu) { |
|
JMenu lastMenu = (JMenu) previousElement; |
|
if (lastMenu.isEnabled() && lastPopup.getComponentCount() > 0) { |
|
|
|
value = 1; |
|
} else { |
|
|
|
value = 3; |
|
} |
|
} |
|
} |
|
if (path.length - value <= 2 |
|
&& !UIManager.getBoolean("Menu.preserveTopLevelSelection")) { |
|
|
|
value = path.length; |
|
} |
|
MenuElement newPath[] = new MenuElement[path.length - value]; |
|
System.arraycopy(path, 0, newPath, 0, path.length - value); |
|
MenuSelectionManager.defaultManager().setSelectedPath(newPath); |
|
} |
|
} |
|
|
|
private static MenuElement nextEnabledChild(MenuElement e[], |
|
int fromIndex, int toIndex) { |
|
for (int i=fromIndex; i<=toIndex; i++) { |
|
if (e[i] != null) { |
|
Component comp = e[i].getComponent(); |
|
if ( comp != null |
|
&& (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable")) |
|
&& comp.isVisible()) { |
|
return e[i]; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
private static MenuElement previousEnabledChild(MenuElement e[], |
|
int fromIndex, int toIndex) { |
|
for (int i=fromIndex; i>=toIndex; i--) { |
|
if (e[i] != null) { |
|
Component comp = e[i].getComponent(); |
|
if ( comp != null |
|
&& (comp.isEnabled() || UIManager.getBoolean("MenuItem.disabledAreNavigable")) |
|
&& comp.isVisible()) { |
|
return e[i]; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
static MenuElement findEnabledChild(MenuElement e[], int fromIndex, |
|
boolean forward) { |
|
MenuElement result; |
|
if (forward) { |
|
result = nextEnabledChild(e, fromIndex+1, e.length-1); |
|
if (result == null) result = nextEnabledChild(e, 0, fromIndex-1); |
|
} else { |
|
result = previousEnabledChild(e, fromIndex-1, 0); |
|
if (result == null) result = previousEnabledChild(e, e.length-1, |
|
fromIndex+1); |
|
} |
|
return result; |
|
} |
|
|
|
static MenuElement findEnabledChild(MenuElement e[], |
|
MenuElement elem, boolean forward) { |
|
for (int i=0; i<e.length; i++) { |
|
if (e[i] == elem) { |
|
return findEnabledChild(e, i, forward); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
static class MouseGrabber implements ChangeListener, |
|
AWTEventListener, ComponentListener, WindowListener { |
|
|
|
Window grabbedWindow; |
|
MenuElement[] lastPathSelected; |
|
|
|
public MouseGrabber() { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
msm.addChangeListener(this); |
|
this.lastPathSelected = msm.getSelectedPath(); |
|
if(this.lastPathSelected.length != 0) { |
|
grabWindow(this.lastPathSelected); |
|
} |
|
} |
|
|
|
void uninstall() { |
|
synchronized (MOUSE_GRABBER_KEY) { |
|
MenuSelectionManager.defaultManager().removeChangeListener(this); |
|
ungrabWindow(); |
|
AppContext.getAppContext().remove(MOUSE_GRABBER_KEY); |
|
} |
|
} |
|
|
|
void grabWindow(MenuElement[] newPath) { |
|
|
|
final Toolkit tk = Toolkit.getDefaultToolkit(); |
|
java.security.AccessController.doPrivileged( |
|
new java.security.PrivilegedAction<Object>() { |
|
public Object run() { |
|
tk.addAWTEventListener(MouseGrabber.this, |
|
AWTEvent.MOUSE_EVENT_MASK | |
|
AWTEvent.MOUSE_MOTION_EVENT_MASK | |
|
AWTEvent.MOUSE_WHEEL_EVENT_MASK | |
|
AWTEvent.WINDOW_EVENT_MASK | sun.awt.SunToolkit.GRAB_EVENT_MASK); |
|
return null; |
|
} |
|
} |
|
); |
|
|
|
Component invoker = newPath[0].getComponent(); |
|
if (invoker instanceof JPopupMenu) { |
|
invoker = ((JPopupMenu)invoker).getInvoker(); |
|
} |
|
grabbedWindow = invoker instanceof Window? |
|
(Window)invoker : |
|
SwingUtilities.getWindowAncestor(invoker); |
|
if(grabbedWindow != null) { |
|
if(tk instanceof sun.awt.SunToolkit) { |
|
((sun.awt.SunToolkit)tk).grab(grabbedWindow); |
|
} else { |
|
grabbedWindow.addComponentListener(this); |
|
grabbedWindow.addWindowListener(this); |
|
} |
|
} |
|
} |
|
|
|
void ungrabWindow() { |
|
final Toolkit tk = Toolkit.getDefaultToolkit(); |
|
|
|
java.security.AccessController.doPrivileged( |
|
new java.security.PrivilegedAction<Object>() { |
|
public Object run() { |
|
tk.removeAWTEventListener(MouseGrabber.this); |
|
return null; |
|
} |
|
} |
|
); |
|
realUngrabWindow(); |
|
} |
|
|
|
void realUngrabWindow() { |
|
Toolkit tk = Toolkit.getDefaultToolkit(); |
|
if(grabbedWindow != null) { |
|
if(tk instanceof sun.awt.SunToolkit) { |
|
((sun.awt.SunToolkit)tk).ungrab(grabbedWindow); |
|
} else { |
|
grabbedWindow.removeComponentListener(this); |
|
grabbedWindow.removeWindowListener(this); |
|
} |
|
grabbedWindow = null; |
|
} |
|
} |
|
|
|
public void stateChanged(ChangeEvent e) { |
|
MenuSelectionManager msm = MenuSelectionManager.defaultManager(); |
|
MenuElement[] p = msm.getSelectedPath(); |
|
|
|
if (lastPathSelected.length == 0 && p.length != 0) { |
|
grabWindow(p); |
|
} |
|
|
|
if (lastPathSelected.length != 0 && p.length == 0) { |
|
ungrabWindow(); |
|
} |
|
|
|
lastPathSelected = p; |
|
} |
|
|
|
public void eventDispatched(AWTEvent ev) { |
|
if(ev instanceof sun.awt.UngrabEvent) { |
|
|
|
cancelPopupMenu( ); |
|
return; |
|
} |
|
if (!(ev instanceof MouseEvent)) { |
|
|
|
return; |
|
} |
|
MouseEvent me = (MouseEvent) ev; |
|
Component src = me.getComponent(); |
|
switch (me.getID()) { |
|
case MouseEvent.MOUSE_PRESSED: |
|
if (isInPopup(src) || |
|
(src instanceof JMenu && ((JMenu)src).isSelected())) { |
|
return; |
|
} |
|
if (!(src instanceof JComponent) || |
|
! (((JComponent)src).getClientProperty("doNotCancelPopup") |
|
== BasicComboBoxUI.HIDE_POPUP_KEY)) { |
|
// Cancel popup only if this property was not set. |
|
// If this property is set to TRUE component wants |
|
|
|
cancelPopupMenu(); |
|
// Ask UIManager about should we consume event that closes |
|
|
|
boolean consumeEvent = |
|
UIManager.getBoolean("PopupMenu.consumeEventOnClose"); |
|
|
|
if(consumeEvent && !(src instanceof MenuElement)) { |
|
me.consume(); |
|
} |
|
} |
|
break; |
|
|
|
case MouseEvent.MOUSE_RELEASED: |
|
if(!(src instanceof MenuElement)) { |
|
|
|
if (isInPopup(src)) { |
|
break; |
|
} |
|
} |
|
if(src instanceof JMenu || !(src instanceof JMenuItem)) { |
|
MenuSelectionManager.defaultManager(). |
|
processMouseEvent(me); |
|
} |
|
break; |
|
case MouseEvent.MOUSE_DRAGGED: |
|
if(!(src instanceof MenuElement)) { |
|
// For the MOUSE_DRAGGED event the src is |
|
// the Component in which mouse button was pressed. |
|
// If the src is in popupMenu, |
|
|
|
if (isInPopup(src)) { |
|
break; |
|
} |
|
} |
|
MenuSelectionManager.defaultManager(). |
|
processMouseEvent(me); |
|
break; |
|
case MouseEvent.MOUSE_WHEEL: |
|
if (isInPopup(src) |
|
|| ((src instanceof JComboBox) && ((JComboBox) src).isPopupVisible())) { |
|
|
|
return; |
|
} |
|
cancelPopupMenu(); |
|
break; |
|
} |
|
} |
|
|
|
boolean isInPopup(Component src) { |
|
for (Component c=src; c!=null; c=c.getParent()) { |
|
if (c instanceof Applet || c instanceof Window) { |
|
break; |
|
} else if (c instanceof JPopupMenu) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
void cancelPopupMenu() { |
|
// We should ungrab window if a user code throws |
|
|
|
try { |
|
// 4234793: This action should call firePopupMenuCanceled but it's |
|
// a protected method. The real solution could be to make |
|
|
|
List<JPopupMenu> popups = getPopups(); |
|
for (JPopupMenu popup : popups) { |
|
popup.putClientProperty("JPopupMenu.firePopupMenuCanceled", Boolean.TRUE); |
|
} |
|
MenuSelectionManager.defaultManager().clearSelectedPath(); |
|
} catch (RuntimeException ex) { |
|
realUngrabWindow(); |
|
throw ex; |
|
} catch (Error err) { |
|
realUngrabWindow(); |
|
throw err; |
|
} |
|
} |
|
|
|
public void componentResized(ComponentEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void componentMoved(ComponentEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void componentShown(ComponentEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void componentHidden(ComponentEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void windowClosing(WindowEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void windowClosed(WindowEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void windowIconified(WindowEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void windowDeactivated(WindowEvent e) { |
|
cancelPopupMenu(); |
|
} |
|
public void windowOpened(WindowEvent e) {} |
|
public void windowDeiconified(WindowEvent e) {} |
|
public void windowActivated(WindowEvent e) {} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static class MenuKeyboardHelper |
|
implements ChangeListener, KeyListener { |
|
|
|
private Component lastFocused = null; |
|
private MenuElement[] lastPathSelected = new MenuElement[0]; |
|
private JPopupMenu lastPopup; |
|
|
|
private JRootPane invokerRootPane; |
|
private ActionMap menuActionMap = getActionMap(); |
|
private InputMap menuInputMap; |
|
private boolean focusTraversalKeysEnabled; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean receivedKeyPressed = false; |
|
|
|
void removeItems() { |
|
if (lastFocused != null) { |
|
if(!lastFocused.requestFocusInWindow()) { |
|
// Workarounr for 4810575. |
|
// If lastFocused is not in currently focused window |
|
// requestFocusInWindow will fail. In this case we must |
|
// request focus by requestFocus() if it was not |
|
|
|
Window cfw = KeyboardFocusManager |
|
.getCurrentKeyboardFocusManager() |
|
.getFocusedWindow(); |
|
if(cfw != null && |
|
"###focusableSwingPopup###".equals(cfw.getName())) { |
|
lastFocused.requestFocus(); |
|
} |
|
|
|
} |
|
lastFocused = null; |
|
} |
|
if (invokerRootPane != null) { |
|
invokerRootPane.removeKeyListener(this); |
|
invokerRootPane.setFocusTraversalKeysEnabled(focusTraversalKeysEnabled); |
|
removeUIInputMap(invokerRootPane, menuInputMap); |
|
removeUIActionMap(invokerRootPane, menuActionMap); |
|
invokerRootPane = null; |
|
} |
|
receivedKeyPressed = false; |
|
} |
|
|
|
private FocusListener rootPaneFocusListener = new FocusAdapter() { |
|
public void focusGained(FocusEvent ev) { |
|
Component opposite = ev.getOppositeComponent(); |
|
if (opposite != null) { |
|
lastFocused = opposite; |
|
} |
|
ev.getComponent().removeFocusListener(this); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
*/ |
|
JPopupMenu getActivePopup(MenuElement[] path) { |
|
for (int i=path.length-1; i>=0; i--) { |
|
MenuElement elem = path[i]; |
|
if (elem instanceof JPopupMenu) { |
|
return (JPopupMenu)elem; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
void addUIInputMap(JComponent c, InputMap map) { |
|
InputMap lastNonUI = null; |
|
InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); |
|
|
|
while (parent != null && !(parent instanceof UIResource)) { |
|
lastNonUI = parent; |
|
parent = parent.getParent(); |
|
} |
|
|
|
if (lastNonUI == null) { |
|
c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map); |
|
} else { |
|
lastNonUI.setParent(map); |
|
} |
|
map.setParent(parent); |
|
} |
|
|
|
void addUIActionMap(JComponent c, ActionMap map) { |
|
ActionMap lastNonUI = null; |
|
ActionMap parent = c.getActionMap(); |
|
|
|
while (parent != null && !(parent instanceof UIResource)) { |
|
lastNonUI = parent; |
|
parent = parent.getParent(); |
|
} |
|
|
|
if (lastNonUI == null) { |
|
c.setActionMap(map); |
|
} else { |
|
lastNonUI.setParent(map); |
|
} |
|
map.setParent(parent); |
|
} |
|
|
|
void removeUIInputMap(JComponent c, InputMap map) { |
|
InputMap im = null; |
|
InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); |
|
|
|
while (parent != null) { |
|
if (parent == map) { |
|
if (im == null) { |
|
c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, |
|
map.getParent()); |
|
} else { |
|
im.setParent(map.getParent()); |
|
} |
|
break; |
|
} |
|
im = parent; |
|
parent = parent.getParent(); |
|
} |
|
} |
|
|
|
void removeUIActionMap(JComponent c, ActionMap map) { |
|
ActionMap im = null; |
|
ActionMap parent = c.getActionMap(); |
|
|
|
while (parent != null) { |
|
if (parent == map) { |
|
if (im == null) { |
|
c.setActionMap(map.getParent()); |
|
} else { |
|
im.setParent(map.getParent()); |
|
} |
|
break; |
|
} |
|
im = parent; |
|
parent = parent.getParent(); |
|
} |
|
} |
|
|
|
public void stateChanged(ChangeEvent ev) { |
|
if (!(UIManager.getLookAndFeel() instanceof BasicLookAndFeel)) { |
|
uninstall(); |
|
return; |
|
} |
|
MenuSelectionManager msm = (MenuSelectionManager)ev.getSource(); |
|
MenuElement[] p = msm.getSelectedPath(); |
|
JPopupMenu popup = getActivePopup(p); |
|
if (popup != null && !popup.isFocusable()) { |
|
|
|
return; |
|
} |
|
|
|
if (lastPathSelected.length != 0 && p.length != 0 ) { |
|
if (!checkInvokerEqual(p[0],lastPathSelected[0])) { |
|
removeItems(); |
|
lastPathSelected = new MenuElement[0]; |
|
} |
|
} |
|
|
|
if (lastPathSelected.length == 0 && p.length > 0) { |
|
|
|
JComponent invoker; |
|
|
|
if (popup == null) { |
|
if (p.length == 2 && p[0] instanceof JMenuBar && |
|
p[1] instanceof JMenu) { |
|
|
|
invoker = (JComponent)p[1]; |
|
popup = ((JMenu)invoker).getPopupMenu(); |
|
} else { |
|
return; |
|
} |
|
} else { |
|
Component c = popup.getInvoker(); |
|
if(c instanceof JFrame) { |
|
invoker = ((JFrame)c).getRootPane(); |
|
} else if(c instanceof JDialog) { |
|
invoker = ((JDialog)c).getRootPane(); |
|
} else if(c instanceof JApplet) { |
|
invoker = ((JApplet)c).getRootPane(); |
|
} else { |
|
while (!(c instanceof JComponent)) { |
|
if (c == null) { |
|
return; |
|
} |
|
c = c.getParent(); |
|
} |
|
invoker = (JComponent)c; |
|
} |
|
} |
|
|
|
|
|
lastFocused = KeyboardFocusManager. |
|
getCurrentKeyboardFocusManager().getFocusOwner(); |
|
|
|
// request focus on root pane and install keybindings |
|
|
|
invokerRootPane = SwingUtilities.getRootPane(invoker); |
|
if (invokerRootPane != null) { |
|
invokerRootPane.addFocusListener(rootPaneFocusListener); |
|
invokerRootPane.requestFocus(true); |
|
invokerRootPane.addKeyListener(this); |
|
focusTraversalKeysEnabled = invokerRootPane. |
|
getFocusTraversalKeysEnabled(); |
|
invokerRootPane.setFocusTraversalKeysEnabled(false); |
|
|
|
menuInputMap = getInputMap(popup, invokerRootPane); |
|
addUIInputMap(invokerRootPane, menuInputMap); |
|
addUIActionMap(invokerRootPane, menuActionMap); |
|
} |
|
} else if (lastPathSelected.length != 0 && p.length == 0) { |
|
// menu hidden -- return focus to where it had been before |
|
|
|
removeItems(); |
|
} else { |
|
if (popup != lastPopup) { |
|
receivedKeyPressed = false; |
|
} |
|
} |
|
|
|
|
|
lastPathSelected = p; |
|
lastPopup = popup; |
|
} |
|
|
|
public void keyPressed(KeyEvent ev) { |
|
receivedKeyPressed = true; |
|
MenuSelectionManager.defaultManager().processKeyEvent(ev); |
|
} |
|
|
|
public void keyReleased(KeyEvent ev) { |
|
if (receivedKeyPressed) { |
|
receivedKeyPressed = false; |
|
MenuSelectionManager.defaultManager().processKeyEvent(ev); |
|
} |
|
} |
|
|
|
public void keyTyped(KeyEvent ev) { |
|
if (receivedKeyPressed) { |
|
MenuSelectionManager.defaultManager().processKeyEvent(ev); |
|
} |
|
} |
|
|
|
void uninstall() { |
|
synchronized (MENU_KEYBOARD_HELPER_KEY) { |
|
MenuSelectionManager.defaultManager().removeChangeListener(this); |
|
AppContext.getAppContext().remove(MENU_KEYBOARD_HELPER_KEY); |
|
} |
|
} |
|
} |
|
} |