Thanks guys
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
% 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?
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
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!