Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Customizing uiundo

November 4, 2009 9 Comments

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)
   try
      % R2014a and earlier
      undoObj = uitools.FigureToolManager(hFig);
   catch
      % R2014b and newer
      undoObj = matlab.uitools.internal.FigureToolManager(hFig);
   end
   setappdata(hFig,'uitools_FigureToolManager',undoObj);
end
>> get(undoObj)
    CommandManager: [1x1 uiundo.CommandManager]
            Figure: [1x1 figure]
        UndoUIMenu: [1x1 uimenu]
        RedoUIMenu: [1x1 uimenu]

% Retrieve redo/undo object undoObj = getappdata(hFig,'uitools_FigureToolManager'); if isempty(undoObj) try % R2014a and earlier undoObj = uitools.FigureToolManager(hFig); catch % R2014b and newer undoObj = matlab.uitools.internal.FigureToolManager(hFig); end 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>

% 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>

Menu before customization

Menu after customization

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]}

>> 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)

>> 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;

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;

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)

>> 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:

  1. 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....
  2. uisplittool & uitogglesplittool callbacks – Matlab's undocumented uisplittool and uitogglesplittool are powerful toolbar controls - this article explains how to customize their behavior...
  3. Customizing contour plots part 2 – The contour lines of 3D Matlab plot can be customized in many different ways. This is the 2nd article on this issue. ...
  4. Customizing print setup – Matlab figures print-setup can be customized to automatically prepare the figure for printing in a specific configuration...
  5. Customizing contour plots – Contour labels, lines and fill patches can easily be customized in Matlab HG2. ...
  6. Customizing axes rulers – HG2 axes can be customized in numerous useful ways. This article explains how to customize the rulers. ...
Hidden property Menubar Pure Matlab uitools uiundo Undocumented feature Undocumented function
Print Print
« Previous
Next »
9 Responses
  1. Venkat November 11, 2009 at 07:14 Reply

    Hi Yair,
    Can you please answer my query at
    http://groups.google.com/group/comp.soft-sys.matlab/browse_thread/thread/8bba37279a78b926#

    sorry for the irrevalent post here

    thanks

  2. Jason McMains April 20, 2010 at 04:21 Reply

    Hey Yair,
    I’m trying to implement this in a GUI, and I have the basic functionality working, but I’m having trouble jumping to an undo point earlier than the most recent, i.e. if there are 5 possible undo actions, I would like to jump to the 3rd or 4th instead of the last.

    Also, Do you know how to create the list box used in the function button or the cell button in the editor? its a context menu with a scroll bar on the side. I’d like to use that functionality for an undo dropdown.

    Thanks for any help!
    Jason

    • Jason McMains April 20, 2010 at 04:47 Reply

      small update: I’ve gotten this to work partially by using:

      undoObj.CommandManager.UndoStack(num).execute;

      undoObj.CommandManager.UndoStack(num).execute;

      but the undoObj structure wont update to reflect the changes.

      Thanks Again

      • Jason McMains April 20, 2010 at 05:12

        Sorry for more updates, but here is my current solution

        for i=length(undoObj.CommandManager.UndoStack):-1:num
           undoObj.CommandManager.undo;
        end

        for i=length(undoObj.CommandManager.UndoStack):-1:num undoObj.CommandManager.undo; end

        This does what I need, but it requires cycling through all the previous changes. I’d rather just skip to the desired step, because for my gui, this process is pretty slow.

      • Yair Altman April 20, 2010 at 09:01

        @Jason – when you undo steps, you first need to undo all the actions that followed your desired undo-action, because they might affect the way your undone action behaves. Effectively, you are unrolling the actions at the exact reverse order in which they happened. If you look at any program with an undo list, they all behave the same way (try any editor, for example). I actually think it’s very intuitive.

        Regarding the scrollable undo/redo lists – see the previous post for an example. The scrollbar appears automatically when more than 8 list items are available. This is standard JComboBox behavior (the number 8 is a modifiable property of the JComboBox).

  3. John July 21, 2015 at 02:45 Reply

    Hello Yair,

    thank you for your great guide! I implemented a redo/undo functionality in a small tool a couple of years ago, and it worked great … until now. Unfortunately, newer Matlab versions than 2014a don’t have the uitools_FigureToolManager object anymore, and I couldn’t find any alternatives. Do you (or anybody else) know if just the location has changed, or has the object completely been removed? Are there already any alternatives found to access Matlab’s redo/undo manager?

    Best regards

    • Yair Altman July 21, 2015 at 03:17 Reply

      @John – I think you mean R2014b, not R2014a. In 14b the entire graphics engine changed (HG2) so this is not surprising. I updated the main post, but here is the relevant fix:

      try
         % R2014a and earlier
         undoObj = uitools.FigureToolManager(hFig);
      catch
         % R2014b and newer
         undoObj = matlab.uitools.internal.FigureToolManager(hFig);
      end

      try % R2014a and earlier undoObj = uitools.FigureToolManager(hFig); catch % R2014b and newer undoObj = matlab.uitools.internal.FigureToolManager(hFig); end

  4. Veena Gujjar July 20, 2018 at 11:21 Reply

    Hello Yair,

    Can you please help me. I am using matlab 2017b.

    I am using uiundo function for undo and redo feature. when the debugger is inside the uiundo function i can see UndoStack has the value.

    but once i come out of uiundo function and execute below code:

    % Retrieve redo/undo object
    undoObj = getappdata(hFig,'uitools_FigureToolManager');
    if isempty(undoObj)
       try
          % R2014a and earlier
          undoObj = uitools.FigureToolManager(hFig);
       catch
          % R2014b and newer
          undoObj = matlab.uitools.internal.FigureToolManager(hFig);
       end
       setappdata(hFig,'uitools_FigureToolManager',undoObj);
    end
     
    undoObj.CommandManager.peekundo

    % Retrieve redo/undo object undoObj = getappdata(hFig,'uitools_FigureToolManager'); if isempty(undoObj) try % R2014a and earlier undoObj = uitools.FigureToolManager(hFig); catch % R2014b and newer undoObj = matlab.uitools.internal.FigureToolManager(hFig); end setappdata(hFig,'uitools_FigureToolManager',undoObj); end undoObj.CommandManager.peekundo

    undostack is empty. Can you please help me to resolve the issue.

    • Yair Altman July 20, 2018 at 11:53 Reply

      Veena – After you execute the undo function, of course the undo stack will be empty because the undo command was already executed.

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitable (6) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top