Archive for August, 2009

Figure toolbar components

Thursday, August 27th, 2009

Toolbars are by now a staple of modern GUI design. An unobtrusive list of small icons enables easy access to multiple application actions without requiring large space for textual descriptions. Unfortunately, the built-in documented support for the Matlab toolbars is limited to adding icon buttons via the uipushtool and uitoggletool functions, and new toolbars containing them via the uitoolbar function. In this post I will introduce several additional customizations that rely on undocumented features.

This article will only describe figure toolbars. However, much of the discussion is also relevant to the desktop (Comand Window) toolbars and interested users can adapt it accordingly.

Accessing toolbar buttons – undo/redo

Let’s start by adding undo/redo buttons to the existing figure toolbar. I am unclear why such an elementary feature was not included in the default figure toolbar, but this is a fact that can easily be remedied. In a future post I will describe uiundo, Matlab’s semi-documented support for undo/redo functionality, but for the present let’s assume we already have this functionality set up.

First, let’s prepare our icons, which are basically a green-filled triangle icon and its mirror image:

% Load the Redo icon
icon = fullfile(matlabroot,'/toolbox/matlab/icons/greenarrowicon.gif');
[cdata,map] = imread(icon);
 
% Convert white pixels into a transparent background
map(find(map(:,1)+map(:,2)+map(:,3)==3)) = NaN;
 
% Convert into 3D RGB-space
cdataRedo = ind2rgb(cdata,map);
cdataUndo = cdataRedo(:,[16:-1:1],:);

Now let’s add these icons to the default figure toolbar:

% Add the icon (and its mirror image = undo) to the latest toolbar
hUndo = uipushtool('cdata',cdataUndo, 'tooltip','undo', 'ClickedCallback','uiundo(gcbf,''execUndo'')');
hRedo = uipushtool('cdata',cdataRedo, 'tooltip','redo', 'ClickedCallback','uiundo(gcbf,''execRedo'')');

Undo/redo buttons

Undo/redo buttons

In the preceding screenshot, since no figure toolbar was previously shown, uipushtool added the undo and redo buttons to a new toolbar. Had the figure toolbar been visible, then the buttons would have been added to its right end. Since undo/redo buttons are normally requested near the left end of toolbars, we need to rearrange the toolbar buttons:

hToolbar = findall(hFig,'tag','FigureToolBar');
%hToolbar = get(hUndo,'Parent');  % an alternative
hButtons = findall(hToolbar);
set(hToolbar,'children',hButtons([4:end-4,2,3,end-3:end]));
set(hUndo,'Separator','on');

Undo/redo buttons in their expected positions

Undo/redo buttons in their expected positions

We would normally preserve hUndo and hRedo, and modify their Tooltip and Visible/Enable properties in run-time, based on the availability and name of the latest undo/redo actions:

% Retrieve redo/undo object
undoObj = getappdata(hFig,'uitools_FigureToolManager');
if isempty(undoObj)
   undoObj = uitools.FigureToolManager(hFig);
   setappdata(hFig,'uitools_FigureToolManager',undoObj);
end
 
% Customize the toolbar buttons
latestUndoAction = undoObj.CommandManager.peekundo;
if isempty(latestUndoAction)
   set(hUndo, 'Tooltip','', 'Enable','off');
else
   tooltipStr = ['undo' latestUndoAction.Name];
   set(hUndo, 'Tooltip',tooltipStr, 'Enable','on');
end

We can easily adapt the method I have just shown to modify/update existing toolbar icons: hiding/disabling them etc. based on the application needs at run-time.

Adding non-button toolbar components – undo dropdown

A more advanced customization is required if we wish to present the undo/redo actions in a drop-down (combo-box). Unfortunately, since Matlab only enables adding uipushtools and uitoggletools to toolbars, we need to use a Java component. The drawback of using such a component is that it is inaccessible via the toolbar’s Children property (implementation of the drop-down callback function is left as an exercise to the reader):

% Add undo dropdown list to the toolbar
jToolbar = get(get(hToolbar,'JavaContainer'),'ComponentPeer');
if ~isempty(jToolbar)
   undoActions = get(undoObj.CommandManager.UndoStack,'Name');
   jCombo = javax.swing.JComboBox(undoActions(end:-1:1));
   set(jCombo, 'ActionPerformedCallback', @myUndoCallbackFcn);
   jToolbar(1).add(jCombo,5); %5th position, after printer icon
   jToolbar(1).repaint;
   jToolbar(1).revalidate;
end
 
% Drop-down (combo-box) callback function
function myUndoCallbackFcn(hCombo,hEvent)
   itemIndex = get(hCombo,'SelectedIndex');  % 0=topmost item
   itemName  = get(hCombo,'SelectedItem');
   % user processing needs to be placed here
end

Undo dropdown list

Undo dropdown list

Note that the javax.swing.JComboBox constructor accepts a cell-array of strings (undoActions in the snippet above). A user-defined dropdownlist might be constructed as follows (also see a related CSSM thread):

...
dropdownStrings = {'here', 'there', 'everywhere'};
jCombo = javax.swing.JComboBox(dropdownStrings);
set(jCombo, 'ActionPerformedCallback', @myUndoCallbackFcn);
jToolbar(1).addSeparator;
jToolbar(1).add(jCombo);  % at end, following a separator mark
jToolbar(1).repaint;
jToolbar(1).revalidate;
...

A similar approach can be used to add checkboxes, radio-buttons and other non-button controls.

In next week’s post I will describe how the toolbar can be customized using undocumented functionality to achieve a non-default background, a floating toolbar (“palette”) effect and other interesting customizations. If you have any specific toolbar-related request, I’ll be happy to hear in the comments section below.

EditorMacro v2 – setting Command Window key-bindings

Thursday, August 20th, 2009

Some weeks ago, I introduced my EditorMacro utility as a means of assigning user-defined text strings and runnable macros (callbacks) to keyboard shortcuts in the Matlab Editor. I later showed how EditorMacro can be used to set non-textual editor macros.

Since then, I was contacted by several users with questions, enhancement requests and improvement suggestions. Of these, the most notable was from Perttu Ranta-aho who suggested (and provided sample code for) using the editor’s built-in actions. The next logical step was to extend this to support the editor menus, and then extend again to support Command Window key-bindings. We bounced several increasingly-powerful versions of the utility between us, until arriving at the version I uploaded yesterday, which has the following significant improvements over the first version:

  • several fixes: bugs, edge cases, stability (EDT etc.) etc.
  • support for built-in (native) Editor actions
  • support for built-in (native) Command-Window actions

Built-in native actions

It turns out that the Editor and Command-Window both have some ~200 built-in (native) available actions, about half of them common. Actions are identified by name, which is a lowercase dash-separated description like ’selection-up’ (this format is familiar to Emacs users). Altogether, there are over 300 unique built-in actions which can be used, but only ~100 of them are assigned a default key-bindings. A few dozen actions have multiple assigned key-bindings. For example, the ’selection-up’ action is assigned to both ’shift pressed UP’ (=<shift>-<up>) and ’shift pressed KP_UP’ (=<shift>-<Keypad-up>):

>> [bindings, actions] = EditorMacro
actions = 
...[snip]
 'selection-page-down'     'shift pressed PAGE_DOWN'     'editor native action'
 'selection-page-up'       'shift pressed PAGE_UP'       'editor native action'
 'selection-previous-word'                   {2x1 cell}  'editor native action'
 'selection-up'                              {2x1 cell}  'editor native action'
 'set-read-only'                                     []  'editor native action'
 'set-writable'                                      []  'editor native action'
 'shift-insert-break'      'shift pressed ENTER'         'editor native action'
 'shift-line-left'         'ctrl pressed OPEN_BRACKET'   'editor native action'
 'shift-line-right'        'ctrl pressed CLOSE_BRACKET'  'editor native action'
 'shift-tab-pressed'       'shift pressed TAB'           'editor native action'
...[snip]...
 'toggle-typing-mode'      'pressed INSERT'              'editor native action'
 'uncomment'               'ctrl pressed T'              'editor native action'
 'undo'                    'ctrl pressed Z'              'editor native action'
 'unselect'                'pressed ESCAPE'              'editor native action'
 'adjust-window-bottom'                              []  'cmdwin native action'
 'adjust-window-top'                                 []  'cmdwin native action'
 'beep'                    'ctrl pressed G'              'cmdwin native action'
 'break-interrupt'         'ctrl pressed CANCEL'         'cmdwin native action'
...[snip]...
 'toggle-typing-mode'      'pressed INSERT'              'cmdwin native action'
 'unselect'                'ctrl pressed BACK_SLASH'     'cmdwin native action'
 'new-mfile'               'ctrl pressed N'              'editor menu action'  
 'Figure'                                            []  'editor menu action'  
...[snip]...

Even more interesting, apparently some 200 actions do not have any pre-assigned default key-bindings, such as ’set-read-only’ and ’set-writable’ in the snippet above. Let’s take the ‘match-brace’ action for example. This sounded promising so I assigned it an unused key-binding and indeed found that it can be very useful: if your cursor is placed on a beginning or end of some code, clicking the assigned key-binding will jump the cursor to the other end, and then back again. This works nicely for (..), [..], for..end, try..end, if..end, etc.

>> % Ensure that <Alt>-M is unassigned
>> bindings = EditorMacro('alt m')
bindings = 
   Empty cell array: 0-by-4
 
>> % Assign the key-binding and verify
>> EditorMacro('alt m','match-brace','run');
>> bindings = EditorMacro('alt m')
b = 
  'alt pressed M'  'match-brace'  'run'  'editor native action'

Implementation details

Interested readers of this post are encouraged to look within EditorMacro’s source code and see how the native actions and keybindings were retrieved and modified for the editor, Command-Window and menus. In a nutshell, the native action names and key-bindings are stored in a Java Map object. Here’s a code snippet:

%% Get all available actions even those without any key-binding
function actionNames = getNativeActions(hEditorPane)
  try
    actionNames = {};
    actionKeys = hEditorPane.getActionMap.allKeys;
    actionNames = cellfun(@char,cell(actionKeys),'Uniform',0);
    actionNames = sort(actionNames);
  catch
    % never mind...
  end
 
%% Get all active native shortcuts (key-bindings)
function accelerators = getAccelerators(hEditorPane)
  try
    accelerators = cell(0,2);
    inputMap = hEditorPane.getInputMap;
    inputKeys = inputMap.allKeys;
    accelerators = cell(numel(inputKeys),2);
    for ii = 1 : numel(inputKeys)
      thisKey = inputKeys(ii);
      thisAction = inputMap.get(thisKey);
      accelerators(ii,:) = {char(thisKey), char(thisAction)};
    end
    accelerators = sortrows(accelerators,1);
  catch
    % never mind...
  end

The menu retrieval was more difficult: while it is possible to directly access the menubar reference (jMainPane.getRootPane.getMenuBar), the menu items themselves are not visible until their main menu item is clicked (displayed). The only way I know to access menu actions/keybindings is to read them from the individual menu items (if anyone knows a better way please tell me – perhaps some central key-listener repository?). Therefore, a simulation of the menu-click events is done and the menu hierarchy is traveled recuresively to collect all its actions and key-bindings.

A final note relates to the use of EDT. EDT really deserves a separate post, but in a nutshell it means that any action that affects the GUI needs to be invoked asynchronously (via the EDT) rather than synchronously (on the main Matlab thread). This is no real problem in the editor, but it is indeed an issue in the Command Window: If we do not use EDT there, we get ugly red stack-trace exceptions thrown on the Command Window whenever we run our EditorMacro-assigned macro. Here’s the code snippet that solves this:

try
   % Matlab 7
   %jEditorPane.insert(caretPosition, macro);  % better to use replaceSelection() than insert()
   try
      % Try to dispatch on EDT
      awtinvoke(jEditorPane.java, 'replaceSelection', macro);
   catch
      % no good - try direct invocation
      jEditorPane.replaceSelection(macro);
   end
catch
   % Matlab 6
   %jEditorPane.insert(macro, caretPosition);  % note the reverse order of input args vs. Matlab 7...
   try
      % Try to dispatch on EDT
      awtinvoke(jEditorPane.java, 'replaceRange', macro, jEditorPane.getSelStart, jEditorPane.getSelEnd);
   catch
      % no good - try direct invocation
      jEditorPane.replaceRange(macro, jEditorPane.getSelStart, jEditorPane.getSelEnd);
   end
end

Known limitations

Some limitations remain in EditorMacro – here are the major ones:

  • Multi-key bindings are still not reported properly, nor can they be assigned. For example, the editor menu action ‘to-lower-case’ has a pre-assigned default key-binding of <Alt>-<U>-<L>, but this is reported as unassigned. Of course, you can always add another (single-key) assignment for this action, for example: <Alt>-<Ctrl>-<L>
  • Menus are dynamically recreated whenever the Editor is docked/undocked, or a separate type of file is edited (e.g., switching from an m-file to a c-file). Similarly, whenever the active desktop window changes from the Command Window to another desktop-docked window (e.g., Command History). In all these cases, the dynamically recreated menus override any conflicting key-binding previously done with EditorMacro.
  • Unfortunately, my Matlab 6-installed computer crashed, so while the first version of EditorMacro works on ML6, the new version might well not. If you have ML6 available, please email me so I can post a working version with your help. This also means I won’t be able to post much about undocumented ML6 stuff in the future. Perhaps this is Fate’s way of telling me to progress with the times…
  • Key bindings are sometimes lost when switching between a one-document editor and a two-document one (i.e., adding/closing the second doc)
  • Key bindings are not saved between editor sessions
  • In split-pane mode, when inserting a text macro on the secondary (right/bottom) editor pane, then both panes (and the actual document) are updated but the secondary pane does not display the inserted macro (the primary pane looks ok).

The first couple of limitations have a non-perfect workaround that Perttu came up with. He implemented this in his KeyBindings utility on the Matlab File Exchange. Perhaps one day Perttu or me will find the time to merge these utilities into one.

Have you designed some crafty user-defined macro? or found some important unassigned built-in action? Please share your experience using EditorMacro in the comments section below.

Setting listbox mouse actions

Thursday, August 13th, 2009

Matlab listbox uicontrols enable basic mouse support, by exposing the ButtonDownFcn callback property. When set, this callback is activated whenever a mouse button (left or right-click) is pressed within the listbox confines. Often this is enough, but in some cases not: since right-clicks do not modify the selected listbox item, we can trap right-click events, but we cannot know which item was clicked-on, as recently noted on the CSSM forum.

Another limitation of the basic Matlab listbox is that it only enables simple static TooltipString and UIContextMenu properties – it would be much more useful to have dynamic tooltips and context-menus based on the item on which the mouse actually hovers.

Processing mouse right-clicks

These limitations, and many others, can be overcome using the underlying Java component of the Matlab uicontrol. We can get this Java component using my FindJObj utility on the Matlab File Exchange. We can then use the many exposed Java component callback hooks to trap our desired mouse-click or mouse-movement event.

Let’s start with a simple Matlab-code callback code that displays the clicked item and the mouse-click type:

% Prepare the Matlab listbox uicontrol
hFig = figure;
listItems = {'apple','orange','banana','lemon','cherry','pear','melon'};
hListbox = uicontrol(hFig, 'style','listbox', 'pos',[20,20,60,60], 'string',listItems);
 
% Get the listbox's underlying Java control
jScrollPane = findjobj(hListbox);
 
% We got the scrollpane container - get its actual contained listbox control
jListbox = jScrollPane.getViewport.getComponent(0);
 
% Convert to a callback-able reference handle
jListbox = handle(jListbox, 'CallbackProperties');
 
% Set the mouse-click callback
% Note: MousePressedCallback is better than MouseClickedCallback
%       since it fires immediately when mouse button is pressed,
%       without waiting for its release, as MouseClickedCallback does
set(jListbox, 'MousePressedCallback',{@myCallbackFcn,hListbox});
 
% Define the mouse-click callback function
function myCallbackFcn(jListbox,jEventData,hListbox)
   % Determine the click type
   % (can similarly test for CTRL/ALT/SHIFT-click)
   if jEventData.isMetaDown  % right-click is like a Meta-button
      clickType = 'Right-click';
   else
      clickType = 'Left-click';
   end
 
   % Determine the current listbox index
   % Remember: Java index starts at 0, Matlab at 1
   mousePos = java.awt.Point(jEventData.getX, jEventData.getY);
   clickedIndex = jListbox.locationToIndex(mousePos) + 1;
   listValues = get(hListbox,'string');
   clickedValue = listValues{clickedIndex};
 
   fprintf('%s on item #%d (%s)\n', clickType, clickedIndex, clickedValue);
end  % mousePressedCallback

Setting dynamic right-click context menu

Some months ago I explained how to set a context (right-click) menu on a uitree control. I shall now show how to implement a similar dynamic context-menu on a listbox. The code is an extension of the segment resented above:

% Prepare the context menu (note the use of HTML labels)
menuItem1 = javax.swing.JMenuItem('action #1');
menuItem2 = javax.swing.JMenuItem('<html><b>action #2');
menuItem3 = javax.swing.JMenuItem('<html><i>action #3');
 
% Set the menu items' callbacks
set(menuItem1,'ActionPerformedCallback',@myFunc1);
set(menuItem2,'ActionPerformedCallback',{@myfunc2,data1,data2});
set(menuItem3,'ActionPerformedCallback','disp ''action #3...'' ');
 
% Add all menu items to the context menu (with internal separator)
jmenu = javax.swing.JPopupMenu;
jmenu.add(menuItem1);
jmenu.add(menuItem2);
jmenu.addSeparator;
jmenu.add(menuItem3);
 
% Set the mouse-click event callback
% Note: MousePressedCallback is better than MouseClickedCallback
%       since it fires immediately when mouse button is pressed,
%       without waiting for its release, as MouseClickedCallback does
set(jListbox, 'MousePressedCallback', {@mousePressedCallback,hListbox,jmenu});
 
% Mouse-click callback
function mousePressedCallback(jListbox, jEventData, hListbox, jmenu)
   if jEventData.isMetaDown  % right-click is like a Meta-button
      % Get the clicked list-item
      %jListbox = jEventData.getSource;
      mousePos = java.awt.Point(jEventData.getX, jEventData.getY);
      clickedIndex = jListbox.locationToIndex(mousePos) + 1;
      listValues = get(hListbox,'string');
      clickedValue = listValues{clickedIndex};
 
      % Modify the context menu or some other element
      % based on the clicked item. Here is an example:
      item = jmenu.add(['<html><b><font color="red">' clickedValue]);
 
      % Remember to call jmenu.remove(item) in item callback
      % or use the timer hack shown here to remove the item:
      timerFcn = {@removeItem,jmenu,item};
      start(timer('TimerFcn',timerFcn,'StartDelay',0.2));
 
      % Display the (possibly-modified) context menu
      jmenu.show(jListbox, jEventData.getX, jEventData.getY);
      jmenu.repaint;
   else
      % Left-click - do nothing (do NOT display context-menu)
   end
end  % mousePressedCallback
 
% Remove the extra context menu item after display
function removeItem(hObj,eventData,jmenu,item)
   jmenu.remove(item);
end
 
% Menu items callbacks must receive at least 2 args:
% hObject and eventData – user-defined args follow after these two
function myfunc1(hObject, eventData)
   % ... 
 
function myFunc2(hObject, eventData, myData1, myData2)
   % ...

Listbox dynamic context (right-click) menu

Listbox dynamic context (right-click) menu

Setting dynamic tooltips (trapping mouse movements)

As a final example for today, let’s set a dynamic tooltip message, based on the actual mouse hover position. For this we need to trap the MouseMovedCallback property of the Java control:

% Set the mouse-movement event callback
set(jListbox, 'MouseMovedCallback', {@mouseMovedCallback,hListbox});
 
% Mouse-movement callback
function mouseMovedCallback(jListbox, jEventData, hListbox)
   % Get the currently-hovered list-item
   mousePos = java.awt.Point(jEventData.getX, jEventData.getY);
   hoverIndex = jListbox.locationToIndex(mousePos) + 1;
   listValues = get(hListbox,'string');
   hoverValue = listValues{hoverIndex};
 
   % Modify the tooltip based on the hovered item
   msgStr = sprintf('<html>item #%d: <b>%s</b></html>', hoverIndex, hoverValue);
   set(hListbox, 'Tooltip',msgStr);
end  % mouseMovedCallback

Listbox dynamic tooltip

Listbox dynamic tooltip

Context-Sensitive Help

Wednesday, August 5th, 2009

A recent CSSM thread about implementing a system-wide GUI help system got me working on a post to present Matlab’s built-in hidden/unsupported mechanism for context-sensitive help. There are many different ways in which such a system can be implemented (read that thread for some ideas), so Matlab users are by no means limited to Matlab’s built-in implementation. However, the built-in system certainly merits consideration for its simplicity and usefulness.

We start with Matlab’s cshelp function (%matlabroot%\toolbox\matlab\uitools\cshelp.m). cshelp is semi-documented, meaning that it has a help section but no doc, online help or official support. This useful function was grandfathered (made obsolete) in Matlab 7.4 (R2007a) for an unknown reason and to my knowledge without any replacement. cshelp is entirely based on m-code (no hidden internal or Java code) and is surprisingly compact and readable.

cshelp basically attaches two new properties (CSHelpMode and CSHelpData) to the specified figure (this is done using the schema.prop mechanism which will be described in a separate post), temporarily disables all active uicontrols, modifies the figure’s WindowButtonDownFcn callback and sets the mouse cursor (using setptr – another semi-documented function) to an arrow with a question mark.

Clicking any object in the figure’s main area (beneath the toolbar), causes the modified WindowButtonDownFcn callback to run whatever is stored in the figure’s HelpFcn property (string, @function_handle or {@function_handle, params, …} format). Here is a simple example taken from CSSM (thanks Jérôme):

Hfcn = 'str=get(gco,''type''); title([''Type :'' str])';
set(gcf,'HelpFcn',Hfcn);
th = 0:0.314:2*pi;
plot(th,sin(th),'r-','linewidth',4);
uicontrol('units','normalized', 'position',[.45 .02 .1 .05]);
cshelp(gcf);
set(gcf,'CSHelpMode','on');

Simple context-sensitive help system

Simple context-sensitive help system

In order to exit CSHelp mode, the figure’s CSHelpMode property must be set to ‘off’. However, remember that all the figure’s uicontrols are disabled in CSHelp mode. Therefore, the user may use one or more of the following methods (other tactics are also possible, but the ones below seem intuitive):

  • Set the figure’s KeyPressFcn callback property to catch events (e.g., <ESC> key presses) and reset the CSHelpMode property from within the callback
  • Reset the CSHelpMode property at the end of the HelpFcn callback
  • Add a CS Help entry/exit option to the figure’s Help main menu
  • Add a CS Help entry/exit button to the figure toolbar

The following code sample implements all of these suggested tactics (the code to synchronize the states of the menu item and toolbar button is not presented):

% Set the <ESC> key press to exit CSHelp mode
keyFcn = ['if strcmp(get(gcbf,''CurrentKey''),''escape''), ' ...
             'set(gcbf,''CSHelpMode'',''off''); ' ...
          'end'];
set(gcf,'keyPressFcn',keyFcn);
 
% Exit CSHelp mode at the end of the CSHelp callback
helpFcn = 'title([''Type :'' get(gco,''type'')]); set(gcbf,''CSHelpMode'',''off'');';
set(gcf,'HelpFcn',helpFcn);
 
% Add a CSHelp button to the figure toolbar
% Note: retrieve the button icon from the CSHelp cursor icon
hToolbar = findall(allchild(gcf),'flat','type','uitoolbar');
oldPtr = getptr(gcf);
ptrData = setptr('help');
set(gcf, oldPtr{:});
icon(:,:,1) = ptrData{4}/2;  % Convert into RGB TrueColor icon
icon(:,:,2) = ptrData{4}/2;
icon(:,:,3) = ptrData{4}/2;
cbFcn = 'set(gcbf,''CSHelpMode'',get(gcbo,''state''))';
csName = 'Context-sensitive help';
uitoggletool(hToolbar,'CData',icon, 'ClickedCallback',cbFcn, 'TooltipString',csName);
 
% Add a CSHelp menu option to the Help main menu
% Note: unlike other main menus, the Help menu tag is empty, so
% ^^^^  findall(gcf,'tag','figMenuHelp') is empty... Therefore,
%       we find this menu by accessing the Help/About menu item
helpAbout = findall(gcf,'tag','figMenuHelpAbout');
helpMenu = get(helpAbout,'parent');
cbFcn = ['if strcmp(get(gcbo,''Checked''),''on''), ' ...
             'set(gcbo,''Checked'',''off''); ' ...
         'else, ' ...
             'set(gcbo,''Checked'',''on''); ' ...
         'end; ' ...
         'set(gcbf,''CSHelpMode'',get(gcbo,''checked''))'];
uimenu(helpMenu,'Label',csName,'Callback',cbFcn,'Separator','on');

Figure with context-sensitive help action in the main toolbar & menu

Figure with context-sensitive help action in the main toolbar & menu

cshelp has an additional optional argument, accepting another figure handle. This handle, if specified and valid, indicates a parent figure whose CSHelpMode, HelpFcn and HelpTopicMap properties should be shared with this figure (this is done using the handle.listener mechanism which will be described in a separate post). This option is useful when creating a multi-window GUI-wide context-sensitive help system. The user may then activate context-sensitive help in figure A and select the requested context object in figure B.

cshelp would normally be coupled with Matlab’s help system for non-trivial GUI implementations. The undocumented and hidden properties HelpTopicKey (of all handles) and HelpTopicMap (of figures), enable easy tie-in to the CSHelp system. A simplified sample is presented below:

Hfcn=['helpview(get(gco,''HelpTopicKey''),''CSHelpWindow'');'...
      'set(gcbf,''CSHelpMode'',''off'');' ];
set(gcf,'HelpFcn',Hfcn);
th = 0:0.314:2*pi;
hLine = plot(th,sin(th),'r-','linewidth',4);
set(hLine,'HelpTopicKey','MyCSHelpFile.html#Line');
set(gca,  'HelpTopicKey','MyCSHelpFile.html#Axes');

cshelp is by no way limited to presenting Matlab documentation: Refer to helpview’s help section for an in-depth description of help maps and help topics. In a nutshell, helpview accepts any HTML webpage filepath (or webpage internal (#) reference), followed by optional parameter ‘CSHelpWindow’ (that indicates that the specified help page should be displayed in a stand-alone popup window rather than in the desktop’s standard Help tab), and optional extra parameters specifying the popup window’s figure handle and position. The webpage filepath parameter may be replaced by two string parameters, HelpTopicMap filepath and HelpTopicKey. Note that helpview itself is another semi-documented function.