JDK-4670831 : Optimization of dispatched KeyEvents to menus and menu items
  • Type: Enhancement
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2002-04-18
  • Updated: 2003-04-02
  • Resolved: 2003-04-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 tigerFixed
Related Reports
Relates :  
Relates :  
Description
As part of the fix to 4515762 we noticed that there are a lot of keyEvent dispatches originating from the MenuSelectionManager.processKeyEvent. Essentially, the KeyEvent is forwared to every element that could be in the selection path. Before the event is dispatched, the path for that menu element must be constructed. The order for which this event is dispatched starts from the leaves (JMenuItems) and works its way up to the JPopupMenu and JMenu if the event is not consumed.

Part of the solution in 4515762 was to handle KeyEvents intended for the JMenuItems to be handled by the parent JMenu. The JMenu can scan all the JMenuItem elements and dispatch the action to a JMenuItem that has the matching mnemonic. This is fine for JMenuItems which are within the containment of a JMenu but doesn't work for stand alone JPopupMenus. The MenuKeyHandler in BasicMenuItemUI was maintained because of this reason.

The real solution is to consolidate the actions of the MenuKeyHandlers in both the BasicMenuItemUI and BasidMenuUI into a MenuKeyHandler in JPopupMenu. This would require adding semantics to JPopupMenu.processKeyEvent(KeyEvent, MenuElement[], MenuSelectionManager) so that it would translate the KeyEvent into a MenuKeyEvent and dispatch it to a MenuKeyHandler within BasicPopupMenuUI. 

Another pursuit thats worth examining is to look at the shortcutting the keyEvents that originate from BasicPopupMenuUI. All those KeyEvents are dispatched to the MenuSelectionManager. Perhaps we can look at having the BasicPopupMenuUI handling and interpretting KeyEvents to that they can be dispatched to the intended menu item.

Another examination could be changing the semantics of processKeyEvent in MenuSelectionManager. Perhaps the keyEvent can be dispatched to the JMenu or JPopupMenu before the dispatching it to the individual JMenuItems. That would give the parent components a chance to dispatch the event to the correct JMenuItem and consume the event. 

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger FIXED IN: tiger INTEGRATED IN: tiger tiger-b04
24-08-2004

EVALUATION Menu mnemonic handler code has been consolidated from BasicMenuUI and BasicMenuItemUI into BasicPopupMenuUI for ease of maintenance. The proposed API makes JPopuMenu more consistent with the API from JMenuItem/JMenu. In fact, much of the API has been taken from JMenuItem. In the current version, the submenu popup handling code is handled in BasicMenuUI in menuKeyTyped and the mnemonic handling code is in BasicMenuItemUI menuKeyPressed. Now they are both handled in the same MenuKeyListener class within BasicPopupMenuUI. It is belived that this mechanism will shortcut the dispaching of the menu key events. This change is a refactoring of the current mnemonic handling mechanism and will not change existing functionality. Peter Zhelezniakov will be implementing this change. ###@###.### 2003-01-30 Name: pzR10082 Date: 03/20/2003 JPopupMenu.processKeyEvent(KeyEvent, MenuElement, MenuSelectionManager) is reimplemented to notify listeners. So we're able to consolidate mnemonic handling into BasicPopupMenuUI. Individual menu items do not handle mnemonics anymore. BasicMenuUI will handle mnemonics for toplevel menus only. MenuSelectionManager.processKeyEvent() is changed to forward KeyEvents to the very first menu element in selected path, i.e. to selection[0]. ###@###.### ======================================================================
24-08-2004

SUGGESTED FIX bs[] = popupMenu.getSubElements(); ! MenuElement sub = ! BasicPopupMenuUI.findEnabledChild(subs, -1, true); ! if(sub != null) { ! newList.add(sub); } MenuSelectionManager manager = e.getMenuSelectionManager(); MenuElement newPath[] = new MenuElement[0];; *************** *** 508,600 **** } } ! /** ! * Handles the mnemonics for the menu items. Will also handle duplicate mnemonics. ! * Perhaps this should be moved into BasicPopupMenuUI. See 4670831 ! */ ! public void menuKeyPressed(MenuKeyEvent e) { ! if (DEBUG) { ! System.out.println("in BasicMenuUI.menuKeyPressed for " + menuItem.getText()); ! } ! // Handle the case for Escape or Enter... ! char keyChar = e.getKeyChar(); ! if (!Character.isLetterOrDigit(keyChar)) ! return; ! ! MenuSelectionManager manager = e.getMenuSelectionManager(); ! MenuElement path[] = e.getPath(); ! MenuElement selectedPath[] = manager.getSelectedPath(); ! ! for (int i = selectedPath.length - 1; i >=0; i--) { ! if (selectedPath[i] == menuItem) { ! JPopupMenu popupMenu = ((JMenu)menuItem).getPopupMenu(); ! if(!popupMenu.isVisible()) { ! return; // Do not invoke items from invisible popup ! } ! MenuElement items[] = popupMenu.getSubElements(); ! ! MenuElement currentItem = selectedPath[selectedPath.length - 1]; ! int currentIndex = -1; ! int matches = 0; ! int firstMatch = -1; ! int indexes[] = null; ! ! for (int j = 0; j < items.length; j++) { ! int key = ((JMenuItem)items[j]).getMnemonic(); ! if(lower((char)key) == lower(keyChar)) { ! if (matches == 0) { ! firstMatch = j; ! matches++; ! } else { ! if (indexes == null) { ! indexes = new int[items.length]; ! indexes[0] = firstMatch; ! } ! indexes[matches++] = j; ! } ! } ! if (currentItem == items[j]) { ! currentIndex = matches - 1; ! } ! } ! ! if (matches == 0) { ! ; // no op (consume) ! } else if (matches == 1) { ! // Invoke the menu action ! JMenuItem item = (JMenuItem)items[firstMatch]; ! if (!(item instanceof JMenu)) { ! // Let Submenus be handled by menuKeyTyped ! manager.clearSelectedPath(); ! item.doClick(); ! } ! } else { ! // Select the menu item with the matching mnemonic. If ! // the same mnemonic has been invoked then select the next ! // menu item in the cycle. ! MenuElement newItem = null; ! ! newItem = items[indexes[(currentIndex + 1) % matches]]; ! ! MenuElement newPath[] = new MenuElement[path.length+2]; ! System.arraycopy(path, 0, newPath, 0, path.length); ! newPath[path.length] = popupMenu; ! newPath[path.length+1] = newItem; ! manager.setSelectedPath(newPath); ! } ! e.consume(); ! return; ! } ! } ! } ! public void menuKeyReleased(MenuKeyEvent e) {} - - private char lower(char keyChar) { - return Character.toLowerCase(keyChar); } } - } --- 493,502 ---- } } ! public void menuKeyPressed(MenuKeyEvent e) {} public void menuKeyReleased(MenuKeyEvent e) {} } } *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java- Thu Mar 20 15:55:28 2003 --- BasicPopupMenuUI.java Thu Mar 20 15:31:41 2003 *************** *** 39,62 **** */ public class BasicPopupMenuUI extends PopupMenuUI { protected JPopupMenu popupMenu = null; static boolean menuKeyboardHelperInstalled = false; static MenuKeyboardHelper menuKeyboardHelper = null; - 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); - } - - - public static ComponentUI createUI(JComponent x) { return new BasicPopupMenuUI(); } --- 39,50 ---- */ public class BasicPopupMenuUI extends PopupMenuUI { protected JPopupMenu popupMenu = null; + private transient PopupMenuListener popupMenuListener = null; + private MenuKeyListener menuKeyListener = null; static boolean menuKeyboardHelperInstalled = false; static MenuKeyboardHelper menuKeyboardHelper = null; public static ComponentUI createUI(JComponent x) { return new BasicPopupMenuUI(); } *************** *** 83,95 **** } protected void installListeners() { if (mouseGrabber == null) { mouseGrabber = new MouseGrabber(); } - if (basicPopupMenuListener == null) { - basicPopupMenuListener = createPopupMenuListener(); - } - popupMenu.addPopupMenuListener(basicPopupMenuListener); if (!menuKeyboardHelperInstalled) { if (menuKeyboardHelper == null) { --- 71,89 ---- } protected void installListeners() { + if (popupMenuListener == null) { + popupMenuListener = new BasicPopupMenuListener(); + } + popupMenu.addPopupMenuListener(popupMenuListener); + + if (menuKeyListener == null) { + menuKeyListener = new BasicMenuKeyListener(); + } + popupMenu.addMenuKeyListener(menuKeyListener); + if (mouseGrabber == null) { mouseGrabber = new MouseGrabber(); } if (!menuKeyboardHelperInstalled) { if (menuKeyboardHelper == null) { *************** *** 167,175 **** } protected void uninstallListeners() { ! if (basicPopupMenuListener != null) { ! popupMenu.removePopupMenuListener(basicPopupMenuListener); } if(mouseGrabber != null) { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); msm.removeChangeListener(mouseGrabber); --- 161,172 ---- } protected void uninstallListeners() { ! if (popupMenuListener != null) { ! popupMenu.removePopupMenuListener(popupMenuListener); } + if (menuKeyListener != null) { + popupMenu.removeMenuKeyListener(menuKeyListener); + } if(mouseGrabber != null) { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); msm.removeChangeListener(mouseGrabber); *************** *** 195,201 **** return getPreferredSize(c); } ! private static MenuElement getFirstPopup() { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); MenuElement[] p = msm.getSelectedPath(); MenuElement me = null; --- 192,198 ---- return getPreferredSize(c); } ! static MenuElement getFirstPopup() { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); MenuElement[] p = msm.getSelectedPath(); MenuElement me = null; *************** *** 208,214 **** return me; } ! private static JPopupMenu getLastPopup() { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); MenuElement[] p = msm.getSelectedPath(); JPopupMenu popup = null; --- 205,211 ---- return me; } ! static JPopupMenu getLastPopup() { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); MenuElement[] p = msm.getSelectedPath(); JPopupMenu popup = null; *************** *** 220,226 **** return popup; } ! private static List getPopups() { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); MenuElement[] p = msm.getSelectedPath(); --- 217,223 ---- return popup; } ! static List getPopups() { MenuSelectionManager msm = MenuSelectionManager.defaultManager(); MenuElement[] p = msm.getSelectedPath(); *************** *** 238,251 **** && ((e.getModifiers() & MouseEvent.BUTTON3_MASK)!=0)); } ! // for auditory feedback ! private transient PopupMenuListener basicPopupMenuListener = null; ! // for auditory feedback ! private PopupMenuListener createPopupMenuListener() { ! return new BasicPopupMenuListener(); } /** * This Listener fires the Action that provides the correct auditory * feedback. --- 235,254 ---- && ((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); + } + /** * This Listener fires the Action that provides the correct auditory * feedback. *************** *** 275,280 **** --- 278,382 ---- } } + /** + * Handles mnemonic for children JMenuItems. + * @since 1.5 + */ + private class BasicMenuKeyListener implements MenuKeyListener { + MenuElement menuToOpen = null; + + public void menuKeyTyped(MenuKeyEvent e) { + if (menuToOpen != null) { + // we have a submenu to open + JPopupMenu subpopup = ((JMenu)menuToOpen).getPopupMenu(); + MenuElement subitem = findEnabledChild( + subpopup.getSubElements(), -1, true); + + ArrayList lst = new ArrayList(Arrays.asList(e.getPath())); + lst.add(menuToOpen); + lst.add(subpopup); + if (subitem != null) { + lst.add(subitem); + } + MenuElement newPath[] = new MenuElement[0];; + newPath = (MenuElement[])lst.toArray(newPath); + MenuSelectionManager.defaultManager().setSelectedPath(newPath); + e.consume(); + } + menuToOpen = null; + } + + public void menuKeyPressed(MenuKeyEvent e) { + // Handle the case for Escape or Enter... + if (!Character.isLetterOrDigit(e.getKeyChar())) { + return; + } + + int keyCode = e.getKeyCode(); + 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]; + if (keyCode == item.getMnemonic()) { + if (matches == 0) { + firstMatch = j; + matches++; + } else { + if (indexes == null) { + indexes = new int[items.length]; + indexes[0] = firstMatch; + } + indexes[matches++] = j; + } + } + if (item.isArmed()) { + currentIndex = matches - 1; + } + } + + if (matches == 0) { + ; // no op (consume) + } else if (matches == 1) { + // Invoke the menu action + JMenuItem item = (JMenuItem)items[firstMatch]; + if (item instanceof JMenu) { + // submenus are handled in menuKeyTyped + menuToOpen = item; + } else if (item.isEnabled()) { + // we have a menu item + manager.clearSelectedPath(); + item.doClick(); + } + } else { + // Select the menu item with the matching mnemonic. If + // the same mnemonic has been invoked then select the next + // menu item in the cycle. + MenuElement newItem = null; + + 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(); + return; + } + + public void menuKeyReleased(MenuKeyEvent e) { + } + } + private static class CancelAction extends AbstractAction { public void actionPerformed(ActionEvent e) { *************** *** 352,358 **** return null; } ! private static MenuElement findEnabledChild(MenuElement e[], int fromIndex, boolean forward) { MenuElement result = null; if (forward) { --- 454,460 ---- return null; } ! static MenuElement findEnabledChild(MenuElement e[], int fromIndex, boolean forward) { MenuElement result = null; if (forward) { *************** *** 366,372 **** return result; } ! private static MenuElement findEnabledChild(MenuElement e[], MenuElement elem, boolean forward) { for (int i=0; i<e.length; i++) { if (e[i] == elem) { --- 468,474 ---- return result; } ! static MenuElement findEnabledChild(MenuElement e[], MenuElement elem, boolean forward) { for (int i=0; i<e.length; i++) { if (e[i] == elem) { ======================================================================
24-08-2004

SUGGESTED FIX Consolidate MenuKeyHandler code into BasicPopupMenuUI from BasicMenuUI and BasicMenuItemUI. ###@###.### 2003-01-30 Name: pzR10082 Date: 03/20/2003 *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/JPopupMenu.java- Thu Mar 20 15:55:26 2003 --- JPopupMenu.java Thu Mar 20 15:54:59 2003 *************** *** 630,635 **** --- 630,668 ---- } /** + * Adds a <code>MenuKeyListener</code> to the popup menu. + * + * @param l the <code>MenuKeyListener</code> to be added + * @since 1.5 + */ + public void addMenuKeyListener(MenuKeyListener l) { + listenerList.add(MenuKeyListener.class, l); + } + + /** + * Removes a <code>MenuKeyListener</code> from the popup menu. + * + * @param l the <code>MenuKeyListener</code> to be removed + * @since 1.5 + */ + public void removeMenuKeyListener(MenuKeyListener l) { + listenerList.remove(MenuKeyListener.class, l); + } + + /** + * Returns an array of all the <code>MenuKeyListener</code>s added + * to this JPopupMenu with addMenuKeyListener(). + * + * @return all of the <code>MenuKeyListener</code>s added or an empty + * array if no listeners have been added + * @since 1.5 + */ + public MenuKeyListener[] getMenuKeyListeners() { + return (MenuKeyListener[])listenerList.getListeners( + MenuKeyListener.class); + } + + /** * Notifies <code>PopupMenuListener</code>s that this popup menu will * become visible. */ *************** *** 1238,1250 **** public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {} /** ! * This method is required to conform to the ! * <code>MenuElement</code> interface, but it not implemented. ! * @see MenuElement#processKeyEvent(KeyEvent, MenuElement[], MenuSelectionManager) */ ! public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) { } /** * Messaged when the menubar selection changes to activate or * deactivate this menu. This implements the --- 1271,1367 ---- public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {} /** ! * Processes a key event forwarded from the ! * <code>MenuSelectionManager</code> and changes the menu selection, ! * if necessary, by using <code>MenuSelectionManager</code>'s API. ! * <p> ! * Note: you do not have to forward the event to sub-components. ! * This is done automatically by the <code>MenuSelectionManager</code>. ! * ! * @param e a <code>KeyEvent</code> ! * @param path the <code>MenuElement</code> path array ! * @param manager the <code>MenuSelectionManager</code> */ ! public void processKeyEvent(KeyEvent e, MenuElement path[], ! MenuSelectionManager manager) { ! MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(), ! e.getWhen(), e.getModifiers(), ! e.getKeyCode(), e.getKeyChar(), ! path, manager); ! processMenuKeyEvent(mke); ! ! if (mke.isConsumed()) { ! e.consume(); } + } + /** + * Handles a keystroke in a menu. + * + * @param e a <code>MenuKeyEvent</code> object + * @since 1.5 + */ + private void processMenuKeyEvent(MenuKeyEvent e) { + switch (e.getID()) { + case KeyEvent.KEY_PRESSED: + fireMenuKeyPressed(e); break; + case KeyEvent.KEY_RELEASED: + fireMenuKeyReleased(e); break; + case KeyEvent.KEY_TYPED: + fireMenuKeyTyped(e); break; + default: + break; + } + } + + /** + * Notifies all listeners that have registered interest for + * notification on this event type. + * + * @param event a <code>MenuKeyEvent</code> + * @see EventListenerList + */ + private void fireMenuKeyPressed(MenuKeyEvent event) { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==MenuKeyListener.class) { + ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event); + } + } + } + + /** + * Notifies all listeners that have registered interest for + * notification on this event type. + * + * @param event a <code>MenuKeyEvent</code> + * @see EventListenerList + */ + private void fireMenuKeyReleased(MenuKeyEvent event) { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==MenuKeyListener.class) { + ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event); + } + } + } + + /** + * Notifies all listeners that have registered interest for + * notification on this event type. + * + * @param event a <code>MenuKeyEvent</code> + * @see EventListenerList + */ + private void fireMenuKeyTyped(MenuKeyEvent event) { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==MenuKeyListener.class) { + ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event); + } + } + } + /** * Messaged when the menubar selection changes to activate or * deactivate this menu. This implements the *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/MenuSelectionManager.java- Thu Mar 20 15:55:27 2003 --- MenuSelectionManager.java Thu Mar 20 15:44:09 2003 *************** *** 382,425 **** * @param e a KeyEvent object */ public void processKeyEvent(KeyEvent e) { ! Vector tmp; ! int selectionSize; ! int i,j,d; ! MenuElement menuElement; ! MenuElement subElements[]; ! MenuElement path[]; ! Component mc; ! if (DEBUG) { ! System.out.println("in MenuSelectionManager.processKeyEvent"); } ! tmp = (Vector)selection.clone(); ! selectionSize = tmp.size(); ! for(i=selectionSize - 1 ; i >= 0 ; i--) { ! menuElement = (MenuElement) tmp.elementAt(i); ! subElements = menuElement.getSubElements(); ! path = null; ! for(j = 0, d = subElements.length ; j < d ; j++) { ! if (subElements[j] == null) continue; ! mc = subElements[j].getComponent(); ! if(!mc.isShowing()) ! continue; if(path == null) { - int k; path = new MenuElement[i+2]; ! for(k=0;k<=i;k++) ! path[k] = (MenuElement)tmp.elementAt(k); } ! path[i+1] = subElements[j]; ! subElements[j].processKeyEvent(e,path,this); ! if(e.isConsumed()) return; } } } /** * Return true if c is part of the currently used menu --- 382,426 ---- * @param e a KeyEvent object */ public void processKeyEvent(KeyEvent e) { ! MenuElement[] sel2 = new MenuElement[0]; ! sel2 = (MenuElement[])selection.toArray(sel2); ! int selSize = sel2.length; ! MenuElement[] path; ! if (selSize < 1) { ! return; } ! for (int i=selSize-1; i>=0; i--) { ! MenuElement elem = sel2[i]; ! MenuElement[] subs = elem.getSubElements(); path = null; ! ! for (int j=0; j<subs.length; j++) { ! if (subs[j] == null || !subs[j].getComponent().isShowing()) { continue; ! } ! if(path == null) { path = new MenuElement[i+2]; ! System.arraycopy(sel2, 0, path, 0, i+1); } ! path[i+1] = subs[j]; ! subs[j].processKeyEvent(e, path, this); ! if (e.isConsumed()) { return; } } } + + // finally dispatch event to the first component in path + path = new MenuElement[1]; + path[0] = sel2[0]; + path[0].processKeyEvent(e, path, this); + if (e.isConsumed()) { + return; + } + } /** * Return true if c is part of the currently used menu *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java- Thu Mar 20 15:55:30 2003 --- BasicMenuItemUI.java Thu Mar 20 15:31:42 2003 *************** *** 14,20 **** import javax.swing.*; import javax.swing.event.*; - import javax.swing.border.*; import javax.swing.plaf.*; import javax.swing.text.View; --- 14,19 ---- *************** *** 252,258 **** } protected MenuKeyListener createMenuKeyListener(JComponent c) { ! return new MenuKeyHandler(); } private PropertyChangeListener createPropertyChangeListener(JComponent c) { --- 251,257 ---- } protected MenuKeyListener createMenuKeyListener(JComponent c) { ! return null; } private PropertyChangeListener createPropertyChangeListener(JComponent c) { *************** *** 1009,1051 **** } } } - - private class MenuKeyHandler implements MenuKeyListener { - - /** - * Handles the mnemonic key typed in the MenuItem if this menuItem is in - * a standalone popup menu. This invocation normally - * handled in BasicMenuUI.MenuKeyHandler.menuKeyPressed. Ideally, the - * MenuKeyHandlers for both BasicMenuItemUI and BasicMenuUI can be consolidated - * into BasicPopupMenuUI but that would require an semantic change. This - * would result in a performance win since we can shortcut a lot of the needless - * processing from MenuSelectionManager.processKeyEvent(). See 4670831. - */ - public void menuKeyTyped(MenuKeyEvent e) { - if (DEBUG) { - System.out.println("in BasicMenuItemUI.menuKeyTyped for " + menuItem.getText()); - } - int key = menuItem.getMnemonic(); - if(key == 0 || e.getPath().length != 2) // Hack! Only proceed if in a JPopupMenu - return; - if(lower((char)key) == lower(e.getKeyChar())) { - MenuSelectionManager manager = - e.getMenuSelectionManager(); - doClick(manager); - e.consume(); - } - } - public void menuKeyPressed(MenuKeyEvent e) { - if (DEBUG) { - System.out.println("in BasicMenuItemUI.menuKeyPressed for " + menuItem.getText()); - } - } - public void menuKeyReleased(MenuKeyEvent e) {} - - private char lower(char keyChar) { - return Character.toLowerCase(keyChar); - } - } private class PropertyChangeHandler implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { --- 1008,1013 ---- *** /net/diablo/export2/swing/zpm/webrev/src/share/classes/javax/swing/plaf/basic/BasicMenuUI.java- Thu Mar 20 15:55:29 2003 --- BasicMenuUI.java Thu Mar 20 15:31:42 2003 *************** *** 217,223 **** public void actionPerformed(ActionEvent e) { if (!crossMenuMnemonic) { ! JPopupMenu pm = getActivePopupMenu(); if (pm != null && pm != menu.getParent()) { return; } --- 217,223 ---- public void actionPerformed(ActionEvent e) { if (!crossMenuMnemonic) { ! JPopupMenu pm = BasicPopupMenuUI.getLastPopup(); if (pm != null && pm != menu.getParent()) { return; } *************** *** 457,504 **** public void menuDragMouseReleased(MenuDragMouseEvent e) {} } - static JPopupMenu getActivePopupMenu() { - MenuElement[] path = MenuSelectionManager.defaultManager(). - getSelectedPath(); - for (int i=path.length-1; i>=0; i--) { - MenuElement elem = path[i]; - if (elem instanceof JPopupMenu) { - return (JPopupMenu)elem; - } - } - return null; - } - /** ! * Handles the mnemonic handling for the JMenu and JMenuItems. */ private class MenuKeyHandler implements MenuKeyListener { /** ! * Opens the SubMenu */ public void menuKeyTyped(MenuKeyEvent e) { ! if (DEBUG) { ! System.out.println("in BasicMenuUI.menuKeyTyped for " + menuItem.getText()); ! } ! if (!crossMenuMnemonic) { ! JPopupMenu pm = getActivePopupMenu(); ! if (pm != null && pm != menuItem.getParent()) { return; } - } ! int key = menuItem.getMnemonic(); ! if(key == 0) ! return; MenuElement path[] = e.getPath(); ! if(lower((char)key) == lower(e.getKeyChar())) { JPopupMenu popupMenu = ((JMenu)menuItem).getPopupMenu(); ArrayList newList = new ArrayList(Arrays.asList(path)); newList.add(popupMenu); ! MenuElement sub[] = popupMenu.getSubElements(); ! if(sub.length > 0) { ! newList.add(sub[0]); } MenuSelectionManager manager = e.getMenuSelectionManager(); MenuElement newPath[] = new MenuElement[0];; --- 457,489 ---- public void menuDragMouseReleased(MenuDragMouseEvent e) {} } /** ! * Handles the mnemonic handling for toplevel menus only. ! * Submenus are handled in BasicPopupMenuUI */ private class MenuKeyHandler implements MenuKeyListener { /** ! * Open the menu */ public void menuKeyTyped(MenuKeyEvent e) { ! if (!crossMenuMnemonic && BasicPopupMenuUI.getLastPopup() != null) { ! // when crossMenuMnemonic is not set, we don't open a toplevel ! // menu if another toplevel menu is already open return; } ! char key = Character.toLowerCase((char)menuItem.getMnemonic()); MenuElement path[] = e.getPath(); ! if (key == Character.toLowerCase(e.getKeyChar())) { JPopupMenu popupMenu = ((JMenu)menuItem).getPopupMenu(); ArrayList newList = new ArrayList(Arrays.asList(path)); newList.add(popupMenu); ! MenuElement su
24-08-2004