Last week I discussed uiundo – Matlab’s undocumented undo/redo manager. Today, I will show how this object can be customized for some specific needs. However, we first need to understand a little more about how uiundo works beneath the hood.
Matlab stores all of a figure’s undo/redo data in a hidden figure object, referenced by getappdata(hFig,’uitools_FigureToolManager’). This means that by default uiundo works at the figure level, rather than the application level or the GUI component level. If we wish to modify this default behavior, we need to programmatically inspect and filter the undo/redo actions stack based on the action source. Read below to see how this can be done.
The hidden uitools_FigureToolManager object, defined in %MATLABROOT%\toolbox\matlab\uitools\@uiundo\, uses a stack to store instances of the undo/redo cmd data structure introduced in last week’s post:
% Retrieve redo/undo object undoObj = getappdata(hFig,'uitools_FigureToolManager'); if isempty(undoObj) undoObj = uitools.FigureToolManager(hFig); setappdata(hFig,'uitools_FigureToolManager',undoObj); end >> get(undoObj) CommandManager: [1x1 uiundo.CommandManager] Figure: [1x1 figure] UndoUIMenu: [1x1 uimenu] RedoUIMenu: [1x1 uimenu]
There are several interesting things we can do with this undoObj. First, let’s modify the main-menu items (I will discuss menu customization in more detail in another post):
% Modify the main menu item (similarly for redo/undo) if ~isempty(undoObj.RedoUIMenu) undoObj.RedoUIMenu.Position =1; %default=2 (undo above redo) undoObj.RedoUIMenu.Enable = 'off'; % default='on' undoObj.RedoUIMenu.Checked = 'on'; % default='off' undoObj.RedoUIMenu.ForegroundColor = [1,0,0]; % =red end if ~isempty(undoObj.UndoUIMenu) undoObj.UndoUIMenu.Label = '<html><b><i>Undo action'; % Note: &Undo underlines 'U' and adds a keyboard accelerator % but unfortunately only if the label is non-HTML ... undoObj.UndoUIMenu.Separator = 'on'; % default='off' undoObj.UndoUIMenu.Checked = 'on'; % default='off' undoObj.UndoUIMenu.ForegroundColor = 'blue'; % default=black end </i></b></html>
Clik here to view.

Image may be NSFW.
Clik here to view.
Figure menu before and after customization
Now, let’s take a look at undoObj’s CommandManager child (the Figure child object is simply handle(hFig), and so is not very interesting):
>> undoObj.CommandManager.get UndoStack: [13x1 uiundo.FunctionCommand] RedoStack: [1x1 uiundo.FunctionCommand] MaxUndoStackLength: [] Verbose: [] >> undoObj.CommandManager.UndoStack(end).get Parent: [] MCodeComment: [] Name: 'slider update (0.48 to 0.38)' Function: @internal_update Varargin: {[53.0037841796875] [0.38] [1x1 double]} InverseFunction: @internal_update InverseVarargin: {[53.0037841796875] [0.48] [1x1 double]}
This looks familiar: In fact, it is exactly the cmd data structure being passed to the uiundo function, with the additional (apparently unused) properties Parent and MCodeComment. CommandManager‘s UndoStack and RedoStack child objects contain all stored undo/redo actions such that the latest action is at the end of these arrays. In the snippet above, there are 13 undo-able actions, with the latest action in UndoStack(end). UndoStack and RedoStack have the same structure:
- Name contains the action description (presented in the figure’s menu)
- Function is the function handle that will be invoked if the action is redone
- Varargin are the arguments passed to Function during redo
- InverseFunction is the function handle that will be invoked if the action is undone
- InverseVarargin are the arguments passed to InverseFunction during undo
- Parent and MCodeComment – I could not determine what these are used for
We can inspect the latest undo/redo actions, without activating them, by using CommandManager‘s peekundo() and peekredo() methods (which return empty [] if no undo/redo action is available):
>> undoObj.CommandManager.peekredo.get % first check if isempty Parent: [] MCodeComment: [] Name: 'slider update (0.38 to 0.28)' Function: @internal_update Varargin: {[53.0037841796875] [0.28] [1x1 double]} InverseFunction: @internal_update InverseVarargin: {[53.0037841796875] [0.38] [1x1 double]} >> undoObj.CommandManager.peekundo.get Parent: [] MCodeComment: [] Name: 'slider update (0.48 to 0.38)' Function: @internal_update Varargin: {[53.0037841796875] [0.38] [1x1 double]} InverseFunction: @internal_update InverseVarargin: {[53.0037841796875] [0.48] [1x1 double]} >> undoObj.CommandManager.peekundo.Name ans = slider update (0.48 to 0.38)
We can undo/redo the latest action (last element of the UndoStack/RedoStack) by invoking CommandManager‘s undo()/redo() methods. This is actually what uiundo is doing behind the scenes when it is called with the ‘execUndo’ and ‘execRedo’ arguments:
undoObj.CommandManager.undo; undoObj.CommandManager.redo;
We can clear the entire actions stack by using CommandManager‘s empty() method. This can be useful, for example, after a ‘Save’ or ‘Apply’ operation in our GUI:
undoObj.CommandManager.empty;
If we set CommandManager‘s Verbose property to any non-empty value, debug information is spilled onto the Command Window when new uiundo actions are added:
>> undoObj.CommandManager.Verbose = 1; % now move the slider and see the debug info below: internal_update(h_uicontrol, [0.48,], h_uicontrol); % Called by slider update (0.28 to 0.48) internal_update(h_uicontrol, [0.58,], h_uicontrol); % Called by slider update (0.48 to 0.58)
Finally, CommandManager uses its MaxUndoStackLength property to limit the size of the undo/redo stacks. This property is defined as read-only in %matlabroot%\toolbox\matlab\uitools\@uiundo\@CommandManager\schema.m line #12, so if you wish to programmatically modify this property from its default value of empty (=unlimited), you will need to comment out that line.
Related posts:
- uiundo – Matlab’s undocumented undo/redo manager The built-in uiundo function provides easy yet undocumented access to Matlab's powerful undo/redo functionality. This article explains its usage....
- uisplittool & uitogglesplittool callbacks Matlab's undocumented uisplittool and uitogglesplittool are powerful toolbar controls - this article explains how to customize their behavior...
- Customizing print setup Matlab figures print-setup can be customized to automatically prepare the figure for printing in a specific configuration...
- uisplittool & uitogglesplittool Matlab's undocumented uisplittool and uitogglesplittool are powerful controls that can easily be added to Matlab toolbars - this article explains how...