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

EditorMacro – assign a keyboard macro in the Matlab editor

July 1, 2009 24 Comments

Over the past years there have been quite a few requests to enable keyboard macros and keybinding modifications in the Matlab editor. Some posters have even noted this lack as their main reason to use an external editor. Coincidentally, some weeks ago I was approached by a reader (Grant Roch) to help with assigning some textual editor macros, and with joint work we were able to figure out a basic working mechanism. The latest user comment on this issue was posted on the official Matlab Desktop blog yesterday. All this prompted me to finally post a solution to this need: My EditorMacro utility is now available for download on the Mathworks File Exchange. In this post I will detail some of its inner workings, which rely on undocumented Matlab features.
In a nutshell, EditorMacro sets the KeyPressedCallback property (explained in a previous post) for each of the editor’s document panes, to an internal function. This internal function then checks each keystroke against a list of registered keybindings. The list itself is persisted in the editor object’s hidden ApplicationData property (accessible via the getappdata/setappdata built-in functions). If a match is found, then the associated macro is invoked. Depending on the macro type, some text can be inserted at the current editor caret position, or replacing the selected editor text, or a non-text Matlab function/command can be invoked.
This enables EditorMacro to be used for quickly inserting code templates (header comments, try-catch blocks etc.) or for automating Matlab unit testing.
Here’s a typical usage example: start by defining a simple function that returns a dynamic header comment:

function comment = createHeaderComment(hDocument, eventData)
  timestamp = datestr(now);
  username = getenv('username');
  %computer = getenv('computername');  % unused
  lineStr = repmat('%',1,35);
  comment = sprintf(...
      ['%s\n' ...
       '%% \n' ...
       '%% Name:    functionName\n' ...
       '%% \n' ...
       '%% Desc:    enter description here\n' ...
       '%% \n' ...
       '%% Inputs:  enter inputs here\n' ...
       '%% \n' ...
       '%% Outputs: enter outputs here\n' ...
       '%% \n' ...
       '%% Created: %s\n' ...
       '%% \n' ...
       '%% Author:  %s\n' ...
       '%% \n' ...
       '%s\n'], ...
       lineStr, timestamp, username, lineStr);
end  % createHeaderComment

function comment = createHeaderComment(hDocument, eventData) timestamp = datestr(now); username = getenv('username'); %computer = getenv('computername'); % unused lineStr = repmat('%',1,35); comment = sprintf(... ['%s\n' ... '%% \n' ... '%% Name: functionName\n' ... '%% \n' ... '%% Desc: enter description here\n' ... '%% \n' ... '%% Inputs: enter inputs here\n' ... '%% \n' ... '%% Outputs: enter outputs here\n' ... '%% \n' ... '%% Created: %s\n' ... '%% \n' ... '%% Author: %s\n' ... '%% \n' ... '%s\n'], ... lineStr, timestamp, username, lineStr); end % createHeaderComment

Now define a macro to use this function, and another simple try-catch template macro:

>> EditorMacro('Alt-Control-h', @createHeaderComment);
>> macroStr = 'try\n  % Main code here\ncatch\n  % Exception handling here\nend';
>> b = EditorMacro('Ctrl alt T', macroStr)
b =
    'ctrl alt pressed H'    @createHeaderComment    'text'
    'ctrl alt pressed T'             [1x60 char]    'text'

>> EditorMacro('Alt-Control-h', @createHeaderComment); >> macroStr = 'try\n % Main code here\ncatch\n % Exception handling here\nend'; >> b = EditorMacro('Ctrl alt T', macroStr) b = 'ctrl alt pressed H' @createHeaderComment 'text' 'ctrl alt pressed T' [1x60 char] 'text'

Now start with a blank document and click <Ctrl>-<Alt>-H and <Ctrl>-<Alt>-T. This is the end result:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Name:    functionName
%
% Desc:    enter description here
%
% Inputs:  enter inputs here
%
% Outputs: enter outputs here
%
% Created: 01-Jul-2009 23:31:46
%
% Author:  Yair Altman
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
try
  % Main code here
catch
  % Exception handling here
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Name: functionName % % Desc: enter description here % % Inputs: enter inputs here % % Outputs: enter outputs here % % Created: 01-Jul-2009 23:31:46 % % Author: Yair Altman % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% try % Main code here catch % Exception handling here end

Keybindings are normalized using Java’s built-in javax.swing.KeyStroke.getKeyStroke() method, to enable the user a very wide range of keystroke naming formats (e.g., ‘Alt-Control-T’ or ‘ctrl alt t’).
I have taken great pains to make EditorMacro compatible with all Matlab versions since 6.0 (R12). This was no easy feat: Matlab 7 made some significant changes to the editor layout. Luckily, once I found out how to get a handle to the Matlab 6 editor object (this took some hours of trial-and-error), listing its display hierarchy was simple and the modifications were generally straight-forward, although non-trivial (different quirks due to missing default type-castings, missing eventData in invoked callbacks etc…). Please feel free to look at EditorMacro‘s source code for the clearly-marked Matlab 6 segments.
For the record, here is a snippet showing how to get the editor object in Matlab 6 and 7. Note that the desktop handle is only useful to get the editor handle in Matlab 6 – we need a totally different way in Matlab 6:

try
   % Matlab 7
   jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
   jEditor = jDesktop.getGroupContainer('Editor');
catch
   % Matlab 6
   try
      %desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop;
      % no use: can't get to the editor from the desktop handle in Matlab 6...
      openDocs = com.mathworks.ide.editor.EditorApplication.getOpenDocuments;
      firstDoc = openDocs.elementAt(0);
      jEditor = firstDoc.getParent.getParent.getParent;
   catch
      error('Cannot find Matlab editor handle: possibly no open documents');
   end
end

try % Matlab 7 jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance; jEditor = jDesktop.getGroupContainer('Editor'); catch % Matlab 6 try %desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop; % no use: can't get to the editor from the desktop handle in Matlab 6... openDocs = com.mathworks.ide.editor.EditorApplication.getOpenDocuments; firstDoc = openDocs.elementAt(0); jEditor = firstDoc.getParent.getParent.getParent; catch error('Cannot find Matlab editor handle: possibly no open documents'); end end

Another complication arose due to the different layout used for floating/maximized/tiled document layout in the editor. Yet another was due to the different behavior (at least on Matlab 6) between a one-document and a multi-document editor view.
Due to the way keyboard events are processed by the Matlab editor, KeyPressedCallback needs to be set separately for all the open document panes and split-panes. Since we also wish newly-opened documents to recognize the macro bindings, we need to set the common container ancestor’s ComponentAddedCallback to an internal function that will handle the KeyPressedCallback instrumentation for each newly-opened document. Again, this needs to be done somewhat differently for Matlab 6/7.
Note that EditorMacro relies on the editor’s internal display layout, which is very sensitive to modification between Matlab releases (as it has between Matlab 6 and 7, for example).
All-in-all, I believe EditorMacro provides a useful and long-awaited service, which should in fact be part of the built-in editor.

Related posts:

  1. EditorMacro v2 – setting Command Window key-bindings – The EditorMacro utility was extended to support built-in Matlab Editor and Command-Window actions and key-bindings. This post describes the changes and the implementation details....
  2. Accessing the Matlab Editor – The Matlab Editor can be accessed programmatically, for a wide variety of possible uses - this article shows how....
  3. Non-textual editor actions – The UIINSPECT utility can be used to expand EditorMacro capabilities to non-text-insertion actions. This is how:...
  4. R2009b keyboard bindings – The new Matlab release R2009b includes the ability to customize keyboard bindings for the editor and Command Window. However, there are still some uses for the EditorMacro utility and its variants....
  5. Spicing up the Matlab Editor – Matlab's Editor and Workspace can be enhanced quite significantly using an open-source utility. ...
  6. Variables Editor scrolling – The Matlab Variables Editor can be accessed to provide immediate scrolling to a specified cell location. ...
Desktop Editor Java keyboard
Print Print
« Previous
Next »
24 Responses
  1. Jason McMains July 2, 2009 at 04:47 Reply

    Is there a reason that you use a try-catch statement to differentiate between matlab 6 and 7 as opposed to

    Mversion=ver('MATLAB')
    if str2double(Mversion.Version) >= 7
       %7.0 code
    else %if str2double(Mversion.Version) <7
       %6.0 code
    end

    Mversion=ver('MATLAB') if str2double(Mversion.Version) >= 7 %7.0 code else %if str2double(Mversion.Version) <7 %6.0 code end

    • Yair Altman July 2, 2009 at 12:08 Reply

      @Jason – that’s a good question. There are several reasons I use try-catch extensively:

      1. Performance – try-catch is much faster than programmatic logic testing. This is true in general, and is not specific to Matlab (i.e., it also holds true for Java, C++, …). This is because at the machine-code level, try instrumentation translates into a single trap/wait (or equivalent) instrumentation opcode taking microsecs, whereas actual programmatic testing may take millisecs or longer. This may not seem much, but it adds up, especially within loops or recursive functions.

      2. Correctness – the code difference may not split nicely along the Matlab 6/7 boundary – perhaps some aspects from Matlab 6 made it into Matlab 7.0 and were only changed in Matlab 7.1? using try-catch solves this.

      3. Simplicity – try-catch is a lot simpler to code and less likely to contain bugs than programmatic logic.

      4. Debugging – one can never think of everything in advance. Using copious try-catch blocks and placing breakpoints in the catch sections has yielded so many surprising bugs for me that I’ve lost count. Instead of trying to figure out in advance all the different logic tests, I simply place a try-catch block and let mother nature take its course…

      I believe that getting used to writing code with copious try-catch blocks will give payback sooner rather than later. For me it is now second nature.

      Yair

  2. Neil July 14, 2009 at 13:29 Reply

    Great post — one step closer into the black hole void of the matlab gui. Do you know of any way of running commands other than inserting text, for example, moving the cursor, deleting a word/line, or more dynamic text editing?

    n

    • Yair Altman July 17, 2009 at 07:01 Reply

      @Neil – In answer to your question, I posted a follow-up: http://undocumentedmatlab.com/blog/non-textual-editor-actions/

      Removing text can be done using hDocument.delete(startPos,endPos)

      Enjoy 🙂
      Yair

  3. EditorMacro v2 - setting Command Window key-bindings | Undocumented Matlab August 21, 2009 at 01:35 Reply

    […] The EditorMacro utility was extended to support built-in Matlab Editor and Command-Window actions and key-bindings. This post describes the changes and the implementation details. […]

  4. Andrey October 21, 2009 at 05:58 Reply

    Yair Hi!

    There are also some reasons why using try-catch isn’t good in this case

    1) Simple Code – The code is harder to understand.
    2) Debugging – When using try-catch without taking a look at the caught exception, you might miss an error which isn’t related to Matlab version at all. Since you catch it, you will never see the stack in Matlab.
    3) Case handling – You will eventually need another try-catch for the matlab 6 section, to handle other versions (not 6 or 7). So what you get is basically a lot of recursive try-catch, which makes the code even less understandable.

  5. Yair Altman October 22, 2009 at 16:41 Reply

    Andrey – you are correct: try-catch should be used, but excessive use has drawbacks. I guess this is true for most things in life… As elsewhere, there isn’t a clear-cut definition of how much is “just right” – we should all apply judgment and common-sense.

  6. Mike Katz October 23, 2009 at 07:24 Reply

    RE: the try/catch case, the verLessThan function is meant to address providing version-dependent behavior.

  7. Mike Katz October 23, 2009 at 07:26 Reply

    ..forgot to mention that to @Jason that str2double is dangerous b/c some versions of MATLAB such as R14SP1 don’t have straightfoward numbers, it’s 7.0.1

    • Yair Altman October 23, 2009 at 07:38 Reply

      Unfortunately Mike, varLessThan was only introduced in recent Matlab releases, so if I have an older release I would get an error. Which brings me back to try/catch which works for all Matlab releases 🙂

      • Mike Katz October 23, 2009 at 08:26

        True. It’s on the file exchange for that purpose, but I understand. Just wanted to point it out, though.

  8. Jason McMains October 25, 2009 at 20:28 Reply

    in any version specific code I’ve written, I had actually accounted for the 7.0.1 issue by taking only the first 3 characters max, which would give me 7.0 or 7.5 etc. I had just heavily simplified for posting. so really line 2 should read:

    if str2double(Mversion.Version(1:min([3 end])) >= 7

    if str2double(Mversion.Version(1:min([3 end])) >= 7

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