Programmatic shortcuts manipulation – part 2

Today I will expand last week’s post on customizing Matlab Desktop’s shortcuts. I will show that we can incorporate non-standard controls, and add tooltips and user callbacks in undocumented ways that are not available using the interactive Desktop GUI.

Custom shortcut controls

Custom shortcut controls


Today’s article will focus on the new toolstrip interface of Matlab release R2012b and later; adaptation of the code to R2012a and earlier is relatively easy (in fact, simpler than the toolstrip-based code below).

Displaying the Shortcuts panel

Before we begin to modify shortcuts in the Toolstrip’s shortcuts menu, we need to ensure that the Shortcuts panel is visible and active (in current focus), otherwise our customizations will be ignored or cause an error. There is probably a more direct way of doing this, but a simple way that I found was to edit the current Desktop’s layout to include a directive to display the Shortcuts tab, and then load that layout:

jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
hMainFrame = com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame;
jToolstrip = hMainFrame.getToolstrip;
isOk = jToolstrip.setCurrentTab('shortcuts');
if ~isOk  % i.e., Shortcuts tab is NOT displayed
    % Save the current Desktop layout
    jDesktop.saveLayout('Yair');  pause(0.15);
 
    % Update the layout file to display the Shortcuts tab
    filename = fullfile(prefdir, 'YairMATLABLayout.xml');
    fid = fopen(filename, 'rt');
    txt = fread(fid, '*char')';
    fclose(fid);
    txt = regexprep(txt,'(ShowShortcutsTab=)"[^"]*"','');
    txt = regexprep(txt,'(<Layout [^>]*)>','$1 ShowShortcutsTab="yes">');
    fid = fopen(filename, 'wt');
    fwrite(fid,txt);
    fclose(fid);
 
    % Load the modified layout
    jDesktop.restoreLayout('Yair');  pause(0.15);
 
    % The shortcuts tab should now be visible, so transfer focus to that tab
    jToolstrip.setCurrentTab('shortcuts');
end

Custom controls

As I explained in last week’s post, we can use scUtils.addShortcutToBottom to add a simple push-button shortcut to the relevant category panel within the Shortcuts toolstrip tab. To add custom controls, we can simply add the controls to the relevant shortcut category panel container (a com.mathworks.toolstrip.components.TSPanel object). The standard shortcuts are typically placed in the Shortcuts tab’s second TSPanel (“general”), and other categories have TSPanels of their own.

Now here’s the tricky part about TSPanels: we cannot directly add components to the sectino panel (that would be too easy…): the section panels are composed of an array of internal TSPanels, and we need to add the new controls to those internal panels. However, these panels only contain 3 empty slots. If we try to add more than 3 components, the 4th+ component(s) will simply not be displayed. In such cases, we need to create a new TSPanel to display the extra components.

Here then is some sample code to add a combo-box (drop-down) control:

% First, get the last internal TSPanel within the Shortcuts tab's "general" section panel
% Note: jToolstrip was defined in the previous section above
jShortcutsTab = jToolstrip.getModel.get('shortcuts').getComponent;
jSectionPanel = jShortcutsTab.getSectionComponent(1).getSection.getComponent;  % the TSPanel object "general"
jContainer = jSectionPanel.getComponent(jSectionPanel.getComponentCount-1);
 
% If the last internal TSPanel is full, then prepare a new internal TSPanel next to it
if jContainer.getComponentCount >= 3
    % Create a new empty TSPanel and add it to the right of the last internal TSPanel
    jContainer = com.mathworks.toolstrip.components.TSPanel;
    jContainer.setPreferredSize(java.awt.Dimension(100,72));
    jSectionPanel.add(jContainer);
    jSectionPanel.repaint();
    jSectionPanel.revalidate();
end
 
% Create the new control with a custom tooltip and callback function
optionStrings = {'Project A', 'Project B', 'Project C'};
jCombo = com.mathworks.toolstrip.components.TSComboBox(optionStrings);
jCombo = handle(javaObjectEDT(jCombo), 'callbackproperties'));
set(jCombo, 'ActionPerformedCallback', @myCallbackFunction);
jCombo.setToolTipText('Select the requested project');
 
% Now add the new control to the internal TSPanel
jContainer.add(jCombo);
jContainer.repaint();
jContainer.revalidate();

Custom shortcut controls

Custom shortcut controls

Matlab’s internal com.mathworks.toolstrip.components package contains many embeddable controls, including the following (I emphasized those that I think are most useful within the context of the Shortcuts panel): TSButton, TSCheckBox, TSComboBox, TSDropDownButton (a custom combo-box component), TSFormattedTextField, TSLabel, TSList, TSRadioButton, TSScrollPane, TSSlider, TSSpinner, TSSplitButton, TSTextArea, TSTextField, and TSToggleButton. These controls are in most cases simple wrappers of the corresponding Java Swing controls. For example, TSSpinner extends the standard Swing JSpinner control. In some cases, the controls are more complex: for example, the TSSplitButton is similar to Matlab’s uisplittool control.

Toolstrip controls

Toolstrip controls

In fact, these controls can be used even outside the toolstrip, embedded directly in our Matlab figure GUI, using the javacomponent function. For example:

dataModel = javax.swing.SpinnerNumberModel(125, 15, 225, 0.5);  % defaultValue, minValue, maxValue, stepSize
jSpinner = com.mathworks.toolstrip.components.TSSpinner(dataModel);
jSpinner = handle(javaObjectEDT(jSpinner), 'CallbackProperties');
[hjSpinner, hContainer] = javacomponent(jSpinner, [10,10,60,20], gcf);

You can find additional interesting components within the %matlabroot%/java/jar/toolstrip.jar file, which can be opened in any zip file utility or Java IDE. In fact, whatever controls that you see Matlab uses in its Desktop toolstrip (including galleries etc.) can be replicated in custom tabs, sections and panels of our own design.

Matlab Desktop’s interactive GUI only enables creating simple push-button shortcuts having string callbacks (that are eval‘ed in run-time). Using the undocumented programmatic interface that I just showed, we can include more sophisticated controls, as well as customize those controls in ways that are impossible via the programmatic GUI: add tooltips, set non-string (function-handle) callbacks, enable/disable controls, modify icons in run-time etc.

For example (intentionally showing two separate ways of setting the component properties):

% Toggle-button
jTB = handle(javaObjectEDT(com.mathworks.toolstrip.components.TSToggleButton('Toggle button')), 'CallbackProperties')
jTB.setSelected(true)
jTB.setToolTipText('toggle me!')
jTB.ActionPerformedCallback = @(h,e)doSomething();
jContainer.add(jTB);
 
% Check-box
jCB = handle(javaObjectEDT(com.mathworks.toolstrip.components.TSCheckBox('selected !')), 'CallbackProperties');
set(jCB, 'Selected', true, 'ToolTipText','Please select me!', 'ActionPerformedCallback',{@myCallbackFunction,extraData});
jContainer.add(jCB);

(resulting in the screenshot at the top of this post)

Important note: none of these customizations is saved to file. Therefore, they need to be redone programmatically for each separate Matlab session. You can easily do that by calling the relevant code in your startup.m file.

If you wish me to assist with any customization of the Desktop shortcuts, or any other Matlab aspect, then contact me for a short consultancy.

Happy New Year everybody!

Categories: Desktop, High risk of breaking in future versions, Java, Undocumented feature

Tags: , , ,

Bookmark and SharePrint Print

4 Responses to Programmatic shortcuts manipulation – part 2

  1. Mola says:

    Hi Yair,
    Thank you for this post.
    Using your customization I wanted to add a new section to a tab.
    I tried different things but unfortunately the straightforward-way fails:

    hMainFrame = com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame;
    jToolstrip = hMainFrame.getToolstrip;
    jHomeTab = jToolstrip.getModel.get('home').getComponent;
    jMySection = com.mathworks.toolstrip.sections.FlowToolstripSection('MySection');
    jHomeTab.add(jMySection);  % error
    jHomeTab.repaint();
    jHomeTab.revalidate();

    Using TSPanel instead of FlowToolstripSection leads to a ToolstripSectionComponentWrapper casting error when accessing via jHomeTab.getSectionComponent.

    Is there an easy way to add a new section to a tab?

  2. Ale says:

    What about setting a keyboard shortcut for a custom shortcut?
    I have some shortcut that I use really often, but I have to click on them all the time…

    Ale

  3. Alex says:

    Hello Yair,

    I’m wondering, if there is a possibility to put a new created shortcut to the quick access bar programmatically?
    So we can use our shortcuts inside the Matlab-editor, where the quick access bar is also visible.

Leave a Reply


Your email address will not be published. Required fields are marked *