Posts Tagged ‘Editor’

Accessing the Matlab Editor

Monday, October 19th, 2009

Matlab’s built-in editor, like most other Matlab GUI, is Java-based. As such, it can easily be accessed programmatically. ImageAnalyst, a well-respected member of the Matlab community and a frequent CSSM (newsgroup) and FEX (File Exchange) contributor, recently asked whether it is possible to retrieve the name of the Editor’s currently edited file. The answer is that this is very easy, but I decided to use this opportunity to show how other interesting things can be done with the Editor.

Before we start, it should be made clear that this entire article relies on MathWorks internal implementation of the Editor and Desktop, which may change without prior notice in future Matlab releases. The code below appears to work under Matlab 6 & 7, but users who rely on forward compatibility should be aware of this warning.

We start by retrieving the Editor handle. This can be done in a number of ways. The easiest is via the Matlab desktop:

try
    % Matlab 7
    desktop = com.mathworks.mde.desk.MLDesktop.getInstance;
    jEditor = desktop.getGroupContainer('Editor').getTopLevelAncestor;
    % we get a com.mathworks.mde.desk.MLMultipleClientFrame object
catch
    % Matlab 6
    % Unfortunately, we can't get the Editor handle from the Desktop handle in Matlab 6:
    %desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop;
 
    % So here's the workaround for Matlab 6:
    openDocs = com.mathworks.ide.editor.EditorApplication.getOpenDocuments;  % a java.util.Vector
    firstDoc = openDocs.elementAt(0);  % a com.mathworks.ide.editor.EditorViewContainer object
    jEditor = firstDoc.getParent.getParent.getParent;
    % we get a com.mathworks.mwt.MWTabPanel or com.mathworks.ide.desktop.DTContainer object
end

Now that we have the Editor handle, let’s retrieve its currently open (active) file name from the Editor’s title:

title = jEditor.getTitle;
currentFilename = char(title.replaceFirst('Editor - ',''));

The entire list of open file names can be retrieved in several ways:

% Alternative #1:
edhandle = com.mathworks.mlservices.MLEditorServices;
allEditorFilenames = char(edhandle.builtinGetOpenDocumentNames);
 
% Alternative #2:
openFiles = desktop.getWindowRegistry.getClosers.toArray.cell;
allEditorFilenames = cellfun(@(c)c.getTitle.char,openFiles,'un',0);

At the top-level Editor-window level, we can prevent its resizing, update its status bar, modify its toolbar/menu-bar, control docking and do other similar fun things:

% Actions via built-in methods:
jEditor.setResizable(0);
jEditor.setStatusText('testing 123...');
jEditor.setTitle('This is the Matlab Editor');
 
% Equivalent actions via properties:
set(jEditor, 'Resizable', 'off');
set(jEditor, 'StatusText', 'testing 123...');
set(jEditor, 'Title', 'This is the Matlab Editor');

Actually, the jEditor handle has over 300 invokable methods and close to 200 properties that we can get/set. Perhaps the easiest way to find interesting things we can programmatically do with the Editor handle, is to use my UIInspect utility on the File Exchange:

uiinspect(jEditor);  % or: jEditor.uiinspect
Matlab Editor methods, callbacks and properties as seen by uiinspect (click to zoom)

Matlab Editor methods, callbacks and properties as seen by uiinspect
(click to zoom)

The Editor handle is actually a container for many internal panels (toolbars etc.) and documents. The entire object hierarchy can be seen with another of my File Exchange utilities, FindJObj:

findjobj(jEditor);  % or: jEditor.findjobj
Matlab Editor object hierarchy as seen by findjboj (click to zoom)

Matlab Editor object hierarchy as seen by findjboj (click to zoom)

We can modify text within the open Editor documents, and instrument these document to handle event callbacks. To see how, I refer users to my EditorMacro utility on the Matlab File Exchange.

If you find some other nifty and/or useful things that can be done using the Editor handle, please post them in the comments section below.

R2009b keyboard bindings

Sunday, September 6th, 2009

This weekend, Kenn Orr, Matlab’s desktop development team leader, announced an important and long-awaited feature that becomes available in the upcoming R2009b release: the ability to customize keyboard bindings in the system preferences.

This is indeed an important preference customization, which has often been requested. At first look, it appears that the desktop design team has done a good job of enabling easy keyboard shortcuts customization, saving and loading sets of shortcuts etc. – all in a new easy-to-use preference page.

This need has been addressed in the past by my EditorMacro utility, its expansions (here and here), and Perttu Ranta-aho’s KeyBindings derivative utility.

Kudos for a job apparently well-done for the Matlab dev team aside, there may still be reasons for using EditorMacro and/or KeyBindings:

  • Earlier Matlab versions – those who have a Matlab release earlier than R2009b have no option but to use these utilities. The first version of EditorMacro even supported Matlab 6.0, now almost a decade old!
  • Programmatic access – some users may wish to have programmatic access to the keyboard bindings. For example, by saving sets of bindings in an m-file and accessing any of these sets via GUI or the desktop shortcuts toolbar. Note that in R2009b, these preferences can probably be accessed programmatically via the preferences interface, explained here.
  • Understanding the underlying workings of the Matlab desktop and editor, for those interested in exploring and using these undocumented subjects.

User-contributed utilities, especially those relying on undocumented features like EditorMacro and KeyBindings, will always pale next to sleek GUI preferences which are well-integrated by design. They have a place in niche usages, as explained above, but the hope is that all these needs will eventually be addressed by well-documented integrated features, exactly as happened in this particular case.

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.

Non-textual editor actions

Friday, July 17th, 2009

Following my EditorMacro post a couple of weeks ago, which showed how to assign a keyboard macro to the integrated Matlab Editor, several people have asked me whether it is possible to assign a macro to non-textual actions, in addition to the text insertion/replacement which EditorMacro supports.

The quick answer is yes, with some careful programming. Instead of specifying the end result, I will use this opportunity to illustrate how Java objects (not just the editor) can be inspected for their supported actions/properties.

Our first step is to get the requested Java reference handle. This can be done via the Matlab Command Window (interested readers can look at the EditorMacro.m source code, specifically at its getJEditor() function). However, a much easier way is to assign some macro using EditorMacro and then simply place a breakpoint in EditorMacro’s keyPressedCallback() callback function. Then press an arrow key (or any other key) in the editor, and wait for the breakpoint focus to arrive (don’t forget to clear the breakpoint…). From here on, all our actions will be done in the Command Window.

We now have a variable called jEditorPane, which is a reference to a Java object of type javahandle_withcallbacks. com.mathworks.mde.editor.EditorSyntaxTextPane (in Matlab 7 – it’s something similar in Matlab 6). This is a Matlab wrapper for the basic Java object, used for accessing the callback hooks, as explained in a previous post. In our case we are not interested in this wrapper but in its wrapped object, which is retrieved via Matlab’s built-in java function (java(jEditorPane) or jEditorPane.java). The inspection itself is done using Matlab’s standard tools (inspect, methodsview etc.) or via my UIINSPECT utility. I suggest using UIINSPECT, which displays all the information of the standard tools and lots extra, but I’m of course biased…

uiinspect(jEditorPane.java);

jEditorPane inspection using UIINSPECT (click to see details)

jEditorPane inspection using UIINSPECT (click to see details)

Without diving into all the UIINSPECT options (I shall do this in a dedicated post), we see the supported methods/actions on the left, and properties on the right. It is true that none of them are documented, but many are self-explanatory. For example, the cut()/copy()/paste() methods or the caretPosition/caretColor properties. Any combination of these methods and properties can be used in a user-defined macro.

Let’s do a simple example, setting the <Ctrl-E> combination to a macro moving to the end-of-line (unix-style – equivalent to <End> on Windows), and <Ctrl-Shift-E> to a similar macro doing the same while also selecting the text (like <Shift-End> on Windows). We shall even use the same macro code, by simply checking in the eventData whether the <Shift> key is depressed:

function EOL_Macro(hDocument,eventData)
 
  % Find the position of the next EOL mark
  currentPos = hDocument.getCaretPosition;
  docLength = hDocument.getLength;
  textToEOF = char(hDocument.getTextStartEnd(currentPos,docLength));
  nextEOLPos = currentPos+find(textToEOF<=13,1)-1;  % next CR/LF pos
  if isempty(nextEOLPos)
      % no EOL found (=> move to end-of-file)
      nextEOLPos = docLength;
  end
 
  % Do action based on whether <Shift> was pressed or not
  %get(eventData);
  if eventData.isShiftDown
      % Select to EOL
      hDocument.moveCaretPosition(nextEOLPos);
  else
      % Move to EOL (without selection)
      hDocument.setCaretPosition(nextEOLPos);
  end
 
end  % EOL_Macro

…and now let’s activate this macro in the Matlab Command Window:

>> macros = EditorMacro('ctrl-e',@EOL_Macro,'run');
>> macros = EditorMacro('ctrl-shift-e',@EOL_Macro,'run')
macros = 
    'ctrl alt pressed T'      @(a,b)datestr(now)    'text'
    'ctrl pressed E'          @EOL_Macro            'run' 
    'shift ctrl pressed E'    @EOL_Macro            'run'

Please do explore all the possible actions/properties exposed by the jEditorPane object. Probably the worst that could happen (and very rarely) is that you’ll crash Matlab and need to restart it – no biggy. If you find an interesting macro combination, please post it to the File Excahnge, and/or let us all know by placing a comment below.

EditorMacro – assign a keyboard macro in the Matlab editor

Wednesday, July 1st, 2009

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

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'

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

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

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.