cprintf - display formatted color text in the Command Window

In earlier posts I showed how to modify the Command Window (CW) text and background color, and a very limited method of displaying red (error) and blue (hyperlinked) CW messages. Since then, I was obsessed with finding a better way to CW display text in any color. After lots of trial-and-errors, frustrations and blind alleys - many more than I imagined when I started out - I am now very proud to say that I have solved the problem. My cprintf utility is now available in the File Exchange.

Before I describe the internal implementation, let me show the end result:

cprintf - display styled formatted text in the Command Window

cprintf - display styled formatted text in the Command Window

cprintf relies on ideas presented in the previous two posts mentioned above. Since the CW is a JTextArea which does not enable style segments, I was curious to understand how Matlab is able to display syntax highlighting in it. This is probably done using a dedicated UI class (com.mathworks.mde.cmdwin.CmdWinSyntaxUI), as suggested in the second post. This is an internal Matlab Java class, so we cannot modify it. It seemed like a dead end for a while.

But what if we could fool the UI class to think that our text should be syntax highlighted? at least then we’d have a few more colors to play with (comments=green, strings=purple etc.). So I took a look at the CW’s Document component (that holds all text and style info) and there I saw that Matlab uses several custom attributes with the style and hyperlink information:

  • the SyntaxTokens attribute holds style color strings like ‘Colors_M_Strings’ for strings or ‘CWLink’ for hyperlinks
  • the LinkStartTokens attribute holds the segment start offsets for hyperlinks (-1 for non-hyperlinked, 0+ for hyperlink)
  • the HtmlLink attribute holds the URL target (java.lang.String object) for hyperlinks, or null ([]) for non-hyperlink.

I played a hunch and modified the style of a specific text segment and lo-and-behold, its CW color changed! Unfortunately, I found out that I can’t just fprintf(text) and then modify its style - for some unknown reason Matlab first needs to place the relevant segment in a “styled” mode (or something like this). I tried to fprintf(2,text) to set the red (error) style, but this did not help. But when I prepended a simple hyperlink space character I got what I wanted - I could now modify the subsequent text to any of the predefined syntax highlighting colors/styles.

But is it possible to use any user-defined colors, not just the predefined syntax highlighting colors? I then remembered my reported finding from the first post about CW colors that ‘Colors_M_Strings’ and friends are simply preference color objects that can be set using code like:

>> import com.mathworks.services.*;
>> Prefs.setColorPref('Colors_M_Strings',java.awt.Color(...));

So I played another hunch and tried to set a new custom preference:

>> Prefs.setColorPref('yair',java.awt.Color.green);
>> Prefs.getColorPref('yair')
ans =
java.awt.Color[r=0,g=255,b=0]

So far so good. I now played the hunch and changed the CW text element’s style name from ‘Colors_M_Strings’ to ‘yair’ and luckily the new green color took effect!

So we can now set any style color (and underline it by enclosing the text in a non-target-url hyperlink), as long as we define a style name for it using Prefs.setColorPref. How can we ensure the color uniqueness for multiple colors? The answer was to simply use the integer RGB values of the requested color, something like ‘[47,0,255]‘.

But we still have the hyperlinked (underlined) space before our text - how do we get rid of it? I tried to set the relevant LinkStartTokens entry to -1 but could not: unlike SyntaxTokens which are modifiable Java objects, LinkStartTokens is an immutable numeric vector. I could however set its URL target to null ([]) to prevent the mouse cursor to change when hovering over the space character, but cannot remove the underline. I then had an idea to simply hide the underline by setting the character style to the CW’s background color. The hard part was to come up with this idea - implementation was then relatively easy:

% Get a handle to the Command Window component
mde = com.mathworks.mde.desk.MLDesktop.getInstance;
cw = mde.getClient('Command Window');
xCmdWndView = cw.getComponent(0).getViewport.getComponent(0);

% Store the CW background color as a special color pref
% This way, if the CW bg color changes (via File/Preferences),
% it will also affect existing rendered strs
cwBgColor = xCmdWndView.getBackground;
com.mathworks.services.Prefs.setColorPref('CW_BG_Color',cwBgColor);

% Now update the space character's style to 'CW_BG_Color'
% See within the code: setElementStyle(docElement,'CW_BG_Color',...)

Having thus completed the bulk of the hard detective/inductive work, I now had to contend with several other obstacles before the code could be released to the public:

  • Older Matlab versions (e.g., 7.1 R14) uses the Document style elements slightly differently and I needed to find a solution that will work well on all Matlab 7 versions (this took quite some time…)
  • If the text is not newline (’\n’)-terminated, sometimes it is not rendered properly. Adding a forced CW repaint() invocation helped solve much of this problem, but some quirks still remain (see cprintf’s help section)
  • Multi-line text (’abra \n kadbra’) creates several style elements which needed to be processed separately
  • Debugging the code was very difficult because whenever the debugger stopped at a breakpoint, ‘k>>’ is written to the CW thereby ruining the displayed element! I had to devise non-trivial instrumentation and post-processing (see within the code).
  • Taking care of exception handling, argument processing etc. to ensure foolproof behavior. For example, accepting case-insensitive and partial (unique) style names.

Bottom line: we now have a very simple and intuitive utility that is deceivingly simple, but took a few dozen hours of investigation to develop. I never imagined it would be so difficult when I started, but this just makes the engineering satisfaction greater :-)

Any sufficiently advanced technology is indistinguishable from magic - Arthur C. Clarke

As usual, your comments and feedback are most welcome

Addendum (May 15, 2009): CPRINTF was today chosen as the Matlab Central File Exchange Pick of the Week. That was fast! - thanks Brett :-)

Addendum (May 18, 2009): CPRINTF was today removed from the Pick of the Week list, after internal MathWorks discussions that came to a conclusion that by posting CPRINTF on an official Matlab blog it might appear as an official endorsement of undocumented features. I can certainly understand this, so no hard feelings…

Bookmark and Share

Related posts:

  1. 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....
  2. Changing Matlab’s Command Window colors - part 2 The Matlab Command Window enables a limited degree of inline color customization - this post describes how to use it...
  3. 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....
  4. Setting status-bar text The Matlab desktop and figure windows have a usable statusbar which can only be set using undocumented methods. This post shows how to set the status-bar text....
  5. FindJObj GUI - display container hierarchy The FindJObj utility can be used to present a GUI that displays a Matlab container's internal Java components, properties and callbacks....
  6. Detecting window focus events Matlab does not have any documented method to detect window focus events (gain/loss). This article describes an undocumented way to detect such events....

Tags: ,

PoorSo-soHelpfulVery helpfulExcellent! (3 votes, average: 5.00 out of 5)
Loading ... Loading ...
Bookmark and Share Print Print

9 Responses to “cprintf - display formatted color text in the Command Window”

  1. Ben S Ben S says:

    Yair, If I was wearing a hat, I would have taken it off in your honour! This is an impressive demonstration of obsession, detective work and development.

  2. WOW!! I am sure that a lot of people will download your utility, it is such a common request from worldwide MATLAB users!
    You are a guru !!

  3. junziyang junziyang says:

    Great!
    Is there anyway to control the text displayed in the Editor? For example to display Greak symbols in the comments using LATEX?

  4. Yair Altman Yair Altman says:

    @Junziang - as far as I know you can only use LATEX in plot texts, not the editor/CW

  5. junziyang junziyang says:

    Thanks Yair!

    I think with the same method used in your cprintf, not only the color but the font(font name/font style….) can also be readily changed. So I hope with the next version of cprintf we can also change the font of the text in the CW.

    I like cprintf! Thanks a lot!

  6. Yair Altman Yair Altman says:

    @junziyang - that’s an interesting suggestion - I may indeed try this :-)

  7. [...] to enable display of text in any color, as well as underline, in the Command Window. See my post on cprintf for details. [...]

  8. Swagat Swagat says:

    Hello,

    Thanks a lot for providing this beautiful program. But I am facing some problem with it. On my matlab (Version 7.4.0.287 (R2007a)), I am observing a strange thing. For instance, when I execute following command

    >> cprintf([1,0.5,0],’and multi-\nline orange\n’);
    and multi-
    line orange

    It works … But when I execute the next command, it gives error:

    >> cprintf([0,1.0,0],’This is in green’);
    Element #1:
    ??? Attempt to reference field of non-structure array.

    Error in ==> cprintf>dumpElement at 335
    if ~isjava(docElement), docElement = docElement.java; end

    Error in ==> cprintf at 96
    if ishandle(style), dumpElement(style); return; end

    Again I get error :

    >> cprintf([0.5,1.0,0],’This is in green’);
    This is in green>> cprintf([0,0,1],’This is in green’);
    Element #1:
    ??? Attempt to reference field of non-structure array.

    Error in ==> cprintf>dumpElement at 335
    if ~isjava(docElement), docElement = docElement.java; end

    Error in ==> cprintf at 96
    if ishandle(style), dumpElement(style); return; end

    BUt this one works !!

    >> cprintf(-[0,0,1],’This is in green’);
    This is in green>> help printf

    Can you please look into this anomaly and tell me if I am making any mistake.

    Regards,
    Swagat

  9. Yair Altman Yair Altman says:

    Swagat - you are correct, this is indeed a bug: an edge-case caused by the fact that all the values you’ve entered for the style color (0.0 and 1.0) were also valid handles… I have fixed this and a few other issues - the updated CPRINTF is now online in the File Exchange (use the link contained in the article above).

Leave a Reply


Wrap code fragments inside <pre lang="matlab"> tags, like this:

   <pre lang="matlab">
   a = magic(3);
   sum(a)
   </pre>