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 v2 – setting Command Window key-bindings

August 20, 2009 4 Comments

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

>> [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'

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

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

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.

Related posts:

  1. EditorMacro – assign a keyboard macro in the Matlab editor – EditorMacro is a new utility that enables setting keyboard macros in the Matlab editor. this post details its inner workings....
  2. 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....
  3. Changing Matlab's Command Window colors – Matlab's Command Window foreground and background colors can be modified programmatically, using some of Matlab's undocumented internal Java classes. Here's how....
  4. Bold color text in the Command Window – Matlab Command Window text can be formatted *bold* since R2011b. ...
  5. Another Command Window text color hack – Matlab's fprintf command has an undocumented hack to display orange-colored text. ...
  6. cprintf – display formatted color text in the Command Window – cprintf is a utility that utilized undocumented Matlab desktop functionalities to display color and underline-styled formatted text in the Command Window...
Desktop Editor Java keyboard
Print Print
« Previous
Next »
4 Responses
  1. jo August 21, 2009 at 13:43 Reply

    Thanks!
    EditorMacro has been added to the startup.m file! 🙂
    So far I’ve created few macros that remarkably speed up my work.
    One of them is a macro that executes code between tags embedded in a comment of an m-file – excellent alternative for Edit Run Configuration… + F5 combination during debugging an m-file, but… I cannot solve one problem: automatic saving file before run (eg. to activate fresh breakpoints).
    Your post about internal commands gave me new hope. I’ve made some trials to save m-file programmatically but without effect.

    Do you know if such an action is possible from inside the macro?

    • Yair Altman August 22, 2009 at 12:47 Reply

      @Jo – Here’s a snippet of a sample macro (callback) function:

      function myCallback(jDocument, jEventData, varargin)
         filename = jDocument.getFilename;
         % do some user-defined stuff
       
         % Now save the file:
         com.mathworks.mlservices.MLEditorServices.saveDocument(filename);
      end  % myCallback

      function myCallback(jDocument, jEventData, varargin) filename = jDocument.getFilename; % do some user-defined stuff % Now save the file: com.mathworks.mlservices.MLEditorServices.saveDocument(filename); end % myCallback

      Also look at Perttu’s KeyBindings utility for other usage examples of MLEditorServices.

      Yair

  2. mb January 13, 2012 at 05:38 Reply

    Hi,
    In Matlab 2011 com.mathworks.mlservices.MLEditorServices has no such method as saveDocument.
    Instead of it one can use matlab editor api to save the document:

    filehandle = matlab.desktop.editor.getActive 
    filehandle.save() ;

    filehandle = matlab.desktop.editor.getActive filehandle.save() ;

    More about it can be found here http://blogs.mathworks.com/desktop/2011/05/09/r2011a-matlab-editor-api/ .
    I hope it will save someone’s time.

    Regards,
    mb

    • Yair Altman January 13, 2012 at 05:59 Reply

      This is indeed a very good example of the risks in using internal Matlab components – their API can change dramatically from one release to another, without prior notice.

      Here is the alternative in the latest Matlab release (12a):

      % Get the reference to the editor application
      jEditorApp = com.mathworks.mde.editor.MatlabEditorApplication.getInstance;
      jEditorApp = com.mathworks.mlservices.MLEditorServices.getEditorApplication;  % an alternative
       
      % Save the current file
      jEditorApp.getActiveEditor.save();
      jEditorApp.getActiveEditor.saveAs(filename);

      % Get the reference to the editor application jEditorApp = com.mathworks.mde.editor.MatlabEditorApplication.getInstance; jEditorApp = com.mathworks.mlservices.MLEditorServices.getEditorApplication; % an alternative % Save the current file jEditorApp.getActiveEditor.save(); jEditorApp.getActiveEditor.saveAs(filename);

      Other useful methods of the jEditorApp.getActiveEditor reference are:

      addEventListener(com.mathworks.matlab.api.editor.EditorEventListener)
      addPropertyChangeListener(java.beans.PropertyChangeListener)
      addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
      appendText(java.lang.String)
      bringToFront()
      close()
      closeNoPrompt()
      dispose()
      fireEditorEvent(com.mathworks.matlab.api.editor.EditorEvent)
      firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
      getBreakpointMargin() : com.mathworks.matlab.api.debug.BreakpointMargin
      getCaretPosition() : int
      getComponent() : java.awt.Component
      getDocument() : javax.swing.text.Document
      getExecutionArrowMargin() : com.mathworks.matlab.api.debug.ExecutionArrowMargin
      getLanguage() : com.mathworks.matlab.api.editor.EditorLanguage
      getLength() : int
      getLineNumber() : int
      getLongName() : java.lang.String
      getProperty(java.lang.Object) : java.lang.Object
      getSelection() : java.lang.String
      getShortName() : java.lang.String
      getStorageLocation() : com.mathworks.matlab.api.datamodel.StorageLocation
      getText() : java.lang.String
      getTextComponent() : javax.swing.text.JTextComponent
      getTextWithSystemLineEndings() : java.lang.String
      goToFunction(java.lang.String, java.lang.String)
      goToLine(int, boolean)
      goToLine(int, int)
      goToPositionAndHighlight(int, int)
      insertAndFormatTextAtCaret(java.lang.String)
      insertTextAtCaret(java.lang.String)
      isBuffer() : boolean
      isDirty() : boolean
      isEditable() : boolean
      isMCode() : boolean
      isOpen() : boolean
      lineAndColumnToPosition(int, int) : int
      lockIfOpen() : boolean
      negotiateSave() : boolean
      positionToLineAndColumn(int) : int[]
      putProperty(java.lang.String, java.lang.Object)
      refreshMenus()
      reload()
      reloadAndReturnError() : java.lang.String
      removeEventListener(com.mathworks.matlab.api.editor.EditorEventListener)
      removePropertyChangeListener(java.beans.PropertyChangeListener)
      removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
      replaceText(java.lang.String, int, int)
      save()
      saveAndReturnError() : java.lang.String
      saveAs(java.lang.String)
      saveAsAndReturnError(java.lang.String) : java.lang.String
      setCaretPosition(int)
      setClean()
      setDirtyUntilSave()
      setEditable(boolean)
      setEditorStatusBarText(java.lang.String)
      setSelection(int, int)
      setStatusText(java.lang.String)
      smartIndentContents()
      unlock()

      addEventListener(com.mathworks.matlab.api.editor.EditorEventListener) addPropertyChangeListener(java.beans.PropertyChangeListener) addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) appendText(java.lang.String) bringToFront() close() closeNoPrompt() dispose() fireEditorEvent(com.mathworks.matlab.api.editor.EditorEvent) firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object) getBreakpointMargin() : com.mathworks.matlab.api.debug.BreakpointMargin getCaretPosition() : int getComponent() : java.awt.Component getDocument() : javax.swing.text.Document getExecutionArrowMargin() : com.mathworks.matlab.api.debug.ExecutionArrowMargin getLanguage() : com.mathworks.matlab.api.editor.EditorLanguage getLength() : int getLineNumber() : int getLongName() : java.lang.String getProperty(java.lang.Object) : java.lang.Object getSelection() : java.lang.String getShortName() : java.lang.String getStorageLocation() : com.mathworks.matlab.api.datamodel.StorageLocation getText() : java.lang.String getTextComponent() : javax.swing.text.JTextComponent getTextWithSystemLineEndings() : java.lang.String goToFunction(java.lang.String, java.lang.String) goToLine(int, boolean) goToLine(int, int) goToPositionAndHighlight(int, int) insertAndFormatTextAtCaret(java.lang.String) insertTextAtCaret(java.lang.String) isBuffer() : boolean isDirty() : boolean isEditable() : boolean isMCode() : boolean isOpen() : boolean lineAndColumnToPosition(int, int) : int lockIfOpen() : boolean negotiateSave() : boolean positionToLineAndColumn(int) : int[] putProperty(java.lang.String, java.lang.Object) refreshMenus() reload() reloadAndReturnError() : java.lang.String removeEventListener(com.mathworks.matlab.api.editor.EditorEventListener) removePropertyChangeListener(java.beans.PropertyChangeListener) removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener) replaceText(java.lang.String, int, int) save() saveAndReturnError() : java.lang.String saveAs(java.lang.String) saveAsAndReturnError(java.lang.String) : java.lang.String setCaretPosition(int) setClean() setDirtyUntilSave() setEditable(boolean) setEditorStatusBarText(java.lang.String) setSelection(int, int) setStatusText(java.lang.String) smartIndentContents() unlock()

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