Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is it possible to add "Shift" key to uimenu accelerator?

1 view
Skip to first unread message

Corbin Holland

unread,
Feb 11, 2008, 10:52:03 AM2/11/08
to
I have a uimenu callback that I want to execute on either a
menu select event or a "Ctrl+Shift+N" keystroke combination.
After reading the help files it seems impossible to
accomplish this using the 'Accelerator' property of the
uimenu class. So I successfully implemented this using the
figure's KeyPressFcn callback. Ok so it works and
everything is fine and dandy except I want to display the
string 'Ctrl+Shift+N' next to the menu label. Does anyone
know of a way to do this?

Thanks guys

Yair Altman

unread,
Feb 11, 2008, 6:33:02 PM2/11/08
to
"Corbin Holland" <chollan...@opticalsciences.com> wrote
in message <fopqv3$73g$1...@fred.mathworks.com>...


This is impossible in plain-vanilla Matlab. However, Here's
an undocumented/unsupported hack to do this: First, you need
to understand that the Matlab menu is basically just a
simple wrapper for the much more powerful and flexible Java
menu on which it's based. So let's first get the Java
reference for this menu. This is not easy in the general
case but is easier if the menu item is part of the figure's
main menu. So let's modify the default File/Save accelerator
key from 'Ctrl-S' to 'Alt-Shift-S' as an example:

jf = get(gcf,'JavaFrame');
jmb = jf.fFigureClient.getMenuBar;
% File main menu is the first main menu item => index=0
jFileMenu = jmb.getComponent(0);
% Save menu item is the 5th menu item (separators included)
jSave = jFileMenu.getMenuComponent(4);
inspect(jSave) => just to be sure: label='Save' => good!
% Set a new accelerator key
jAccel = javax.swing.KeyStroke.getKeyStroke('alt shift S'));
jSave.setAccelerator(jAccel);

That's all there is to it - the label is modified
automatically to reflect the new keyboard accelerator key.
More info on setting different combinations of accelerator
keys and modifiers can be found here:
http://java.sun.com/javase/6/docs/api/javax/swing/KeyStroke.html#getKeyStroke(java.lang.String)

There's plenty other similar customizations that can only be
done using the Java menu handle: setting icons, several
dozen callback types, tooltips, background color, font, text
alignment etc. etc. Interested readers may wish to
get/set/inspect/methodsview the jSave handle and/or to read
the documentation for Java's JMenuItem
(http://java.sun.com/javase/6/docs/api/javax/swing/JMenuItem.html
). Interested readers may find my FindJObj and UIInspect
contributions on the File Exchange useful for
debugging/developing these sort of things.

Again, beware: all this is unsupported and may possibly
break in any future Matlab release.

Yair Altman
http://ymasoftware.com

Corbin Holland

unread,
Feb 12, 2008, 1:12:01 PM2/12/08
to
Thank you so much Yair, this does indeed change the
accelerator and is exactly what I was looking for!!. There
is however one large problem...WHEN can you actually execute
this code during the figure initialization process. You
can't do it during the OpeningFcn or the OutputFcn. In fact
it will throw an error if I do it at all until the menu has
been clicked on at least once, gives an error on ...

% Just so you know the menu looks like File->New Project

jf = get(gcf,'JavaFrame');

menuBar = jf.fFigureClient.getMenuBar;
fileMenu = menuBar.getComponent(0);
newProjectMenu = fileMenu.getMenuComponent(0);

??? Java exception occurred:
java.lang.ArrayIndexOutOfBoundsException: No such child: 0

at java.awt.Container.getComponent(Unknown Source)

at javax.swing.JMenu.getMenuComponent(Unknown Source)

It seems like the jvm is not loading the actual menu
components until the menu is actually clicked on. Before
the first click the fileMenu's menuComponent array is empty!
After the menu's update fcn has run at least once then you
can run this code no problem, but I really need to load the
label at the menus creation before it is ever clicked. Do
you have any ideas on how this can be accomplished?

Yair Altman

unread,
Feb 12, 2008, 1:54:02 PM2/12/08
to
"Corbin Holland" <chollan...@opticalsciences.com> wrote
in message <fosnhh$grv$1...@fred.mathworks.com>...

> Thank you so much Yair, this does indeed change the
> accelerator and is exactly what I was looking for!!. There
> is however one large problem...WHEN can you actually execute
> this code during the figure initialization process. You
> can't do it during the OpeningFcn or the OutputFcn. In fact
> it will throw an error if I do it at all until the menu has
> been clicked on at least once, gives an error on ...


I encountered the same Java optimization behavior when I
developed my findjobj utility (available on the File
Exchange). The solution was to programmatically simulate the
menu clicking, using one of several possible methods. Here's
a variation of this sub-function:

%% Get the number of menu sub-elements
function numMenuComponents=getNumMenuComponents(jcontainer)

% The following line will raise an Exception for anything
except menus
numMenuComponents = container.getMenuComponentCount;

% No error so far, so this must be a menu container...
% Note: Menu subitems are not visible until the top-level
(root) menu gets initial focus...
% Try several alternatives, until we get a non-empty menu
(or not...)
% TODO: Improve performance - this takes WAY too long...
if jcontainer.isTopLevelMenu && (numMenuComponents==0)
jcontainer.requestFocus;
numMenuComponents = jcontainer.getMenuComponentCount;
if (numMenuComponents == 0)
drawnow; pause(0.001);
numMenuComponents = container.getMenuComponentCount;
if (numMenuComponents == 0)
jcontainer.setSelected(true);
numMenuComponents=container.getMenuComponentCount;
if (numMenuComponents == 0)
drawnow; pause(0.001);
numMenuComponents =
jcontainer.getMenuComponentCount;
if (numMenuComponents == 0)
jcontainer.doClick; % needed in order to
populate the sub-menu components
numMenuComponents =
jcontainer.getMenuComponentCount;
if (numMenuComponents == 0)
drawnow; %pause(0.001);
numMenuComponents =
jcontainer.getMenuComponentCount;
jcontainer.doClick; % close menu by
re-clicking...
if (numMenuComponents == 0)
drawnow; %pause(0.001);
numMenuComponents =
jcontainer.getMenuComponentCount;
end
else
% ok - found sub-items: reclick to close
% Note: no need to close menu since this
% will be done when focus moves
%jcontainer.doClick; % close menu
end
end
end
jcontainer.setSelected(false); %deselect the menu
end
end
end
end

Alternately, assuming you have the Matlab handle for your
specific menu item, you could try modifying one of it's
displayed properties (e.g., label or checkmark) to force the
menu item to render.

Yair Altman
http://ymasoftware.com

Corbin Holland

unread,
Feb 12, 2008, 7:20:17 PM2/12/08
to
Again, thank you Yair. Using your code I was able through
trial and error to figure out how to make it work. For
those that are interested the following works for JMenu
objects in r2007a running on Windows XP

menu.setSelected(true); % menu is a JMenu java reference
drawnow;
menu.setSelected(false);


Its that simple, the menu will be populated with all of its
JMenuItem and JSeparator components. Oh yea, I also had to
execute this code in the figure's OuputFcn because it still
will not work before the gui had been rendered since I could
not figure out how to force the menuBar to populate itself
with its top level JMenu items. Once the gui is rendered
matlab magically populates the menuBar's JMenu items for me.
I am sure there is a way to force it programatically
beforehand but I don't have time to mess with it. Thanks
again Yair!

0 new messages