Archive for March, 2009

Setting system tray popup messages

Tuesday, March 31st, 2009

Continuing my previous post about setting system-tray icons, I will now show how to set informational popup messages next to these icons.

Asynchronous informational messages can be presented next to the sys-tray icon, in a fashion similar to what we came to expect from modern programs. This could be used to indicate some unexpected event that was detected, or the end of a complex calculation phase. The message title, text and severity icon are all customizable.

Unfortunately, the Java method used to display messages, java.awt.TrayIcon.displayMessage(), expects an object of type java.awt.TrayIcon.MessageType, which is an enumeration within the TrayIcon class. However, Matlab’s dot-notation does not recognize what should have been the following correct notation, so we need to resort to Java reflection:

>> trayIcon.displayMessage('title','info msg',TrayIcon.MessageType.INFO);
??? No appropriate method or public field MessageType for class java.awt.TrayIcon

>> trayIconClasses = trayIcon.getClass.getClasses;
>> trayIconClasses(1)
ans =
class java.awt.TrayIcon$MessageType	<= hurray!!!
>> MessageTypes = trayIconClasses(1).getEnumConstants
MessageTypes =
java.awt.TrayIcon$MessageType[]:
    [java.awt.TrayIcon$MessageType]	<= 1: ERROR
    [java.awt.TrayIcon$MessageType]	<= 2: WARNING
    [java.awt.TrayIcon$MessageType]	<= 3: INFO
    [java.awt.TrayIcon$MessageType]	<= 4: NONE
>> trayIcon.displayMessage('title','info msg',MessageTypes(3));

systray INFO message

and another example, now with a WARNING icon:

systray WARNING message

If the title string is left empty, then neither title nor the severity icon will be displayed. The message can still be manually dismissed by clicking within its boundaries:


systray messages without a title (hence also without a severity icon)
systray messages without a title (hence also without a severity icon)

Informational popup messages are automatically aligned and positioned by the system. Messages are automatically dismissed by the system after some time, if not dismissed by the user first. The exact time is determined by system and user activity and other such external factors. Informational messages replace one another, if the previous message has still not been cleared by the user.

I have created a utility function called SYSTRAY, which is a convenience function that facilitates the setup and update of system tray icons and messages. SYSTRAY (with source code) can be downloaded from the File Exchange.

I would be happy to hear if and how you’re using the new system-tray functionality in your application - let me know below.

Bookmark and Share

UISplitPane

Saturday, March 28th, 2009

One of my many File Exchange submissions, UISplitPane, was chosen yesterday as Matlab Central’s Pick of the Week (thanks Jiro!). UISplitPane was probably my most challenging submission to date. I thought it would be a good point in time to share some of the undocumented features that I used in UISplitPane. Readers are encouraged to download UISplitPane.m and look at its code.

The basic problem which UISplitPane solves is the inability in Matlab to construct a dual pane separated by an interactive divider. Such split-panes are very common in modern GUIs, including Matlab tools, but were never available for Matlab GUIs. Java Swing has the JSplitPane control, but Matlab axes cannot (as yet) be added to Java containers. A similar problem exists for tabbed-panes, and Matlab provided a very innovative solution for this (the semi-documented uitabgroup function), which shall be described in a separate post. Unfortunately, uitabgroup’s solution cannot be applied to the split-pane problem since in JSplitPane’s case, the container overlaps the axes-containing panes.

To make a long story short, the solution was to create an off-screen (invisible) JSplitPane, extract only its narrow central divider sub-component, and place that onscreen using javacomponent. I encourage readers to look at the addDivider() function which does this, setting mouse cursor and other interesting properties.

The Java divider’s reference is then converted into a Matlab handle, so that some extra properties can be added using schema.prop (which [you guessed it] will be described in a later post) and will become visible when using regular get.

Pure-Matlab code then attaches standard Matlab uipanels as sub-panes on either side of the divider. Property linkages (this will be detailed in later posts about schema.prop’s getter/setter functions and handle.listener) ensure that whenever the divider is dragged or programmatically modified, the two sub-panes on its sides will be resized accordingly, together with all their content (axes and controls). Since the two split panes are simple uipanels, they can contain not only axes and controls but also other uisplitpanes, creating a hierarchy of split-panes:

Two levels of UISplitPane, with customized dividers

Two levels of UISplitPane, with customized dividers

UISplitPane makes use of some semi-documented internal helper functions: hgfeval is used in the mouse callbacks, in order to chain the original WindowButton callback (if available); setptr is used to set the mouse pointer (cursor).

UISplitPane behaves nicely in the presence of Mode Managers (zoom, pan, …) by using the figure’s undocumented ‘ModeManager’ property and setting its ‘ButtonDownFilter’ to bypass mode.

Finally, the figure’s undocumented ‘JavaFrame’ property is used to get a reference to the figure’s AxisComponent container, which is needed for setting mouse callbacks that behave better than similar callbacks at the figure level.

All-in-all, UISplitPane is perhaps a good example of combining a variety of unrelated undocumented Matlab features in order to achieve a coherent application.

An interesting side-note: Matlab 7.6 (R2008a) and onward contain a reference to uisplittool and uitogglesplittool in the javacomponent.m and %matlabroot%/bin/registry/hg.xml files. These are not valid functions (built-in or otherwise) and I could not figure out how this functionality can actually be used. At the very least it is certain that it is deeply undocumented and (of course) unsupported, leaving an open mystery for future investigation…

Bookmark and Share

Adding a context-menu to a uitree

Friday, March 27th, 2009

A CSSM poster asked a few days ago whether it was possible to add a context (right-click) menu to uitree nodes. Uitree is an internal, undocumented and unsupported Matlab function, which does not enable easy setting of a uicontextmenu like its uicontrol relatives.

However, using some undocumented Java magic, it is possible to do so. Being undocumented and unsupported, this solution might break in future releases, although this is unlikely since it relies on some very basic Java functionality that is unlikely to be broken soon.

Note that the context-menu in the following example uses HTML formatting. As explained in an earlier post, all Swing components (and by extension, most Matlab uicontrols) support HTML formatting.

% Prepare the context menu (note the use of HTML labels)
menuItem1 = javax.swing.JMenuItem('action #1');
menuItem2 = javax.swing.JMenuItem('<html><b>action #2');
menuItem3 = javax.swing.JMenuItem('<html><i>action #3');

% Set the menu items' callbacks
set(menuItem1,'ActionPerformedCallback',@myFunc1);
set(menuItem2,'ActionPerformedCallback',{@myfunc2,data1,data2});
set(menuItem3,'ActionPerformedCallback','disp ''action #3...'' ');

% Add all menu items to the context menu (with internal separator)
jmenu = javax.swing.JPopupMenu;
jmenu.add(menuItem1);
jmenu.add(menuItem2);
jmenu.addSeparator;
jmenu.add(menuItem3);

% Set the tree mouse-click callback
% Note: MousePressedCallback is better than MouseClickedCallback
%       since it fires immediately when mouse button is pressed,
%       without waiting for its release, as MouseClickedCallback does
set(jtree, 'MousePressedCallback', {@mousePressedCallback,jmenu});

% Set the mouse-press callback
function mousePressedCallback(hTree, eventData, jmenu)
   if eventData.isMetaDown  % right-click is like a Meta-button
      % Get the clicked node
      clickX = eventData.getX;
      clickY = eventData.getY;
      jtree = eventData.getSource;
      treePath = jtree.getPathForLocation(clickX, clickY);
      try
         % Modify the context menu or some other element
         % based on the clicked node. Here is an example:
         node = treePath.getLastPathComponent;
         nodeName = ['Current node: ' char(node.getName)];
         item = jmenu.add(nodeName);

         % remember to call jmenu.remove(item) in item callback
         % or use the timer hack shown here to remove the item:
         timerFcn = {@removeItem,jmenu,item};
         start(timer('TimerFcn',timerFcn,'StartDelay',0.2));
      catch
         % clicked location is NOT on top of any node
         % Note: can also be tested by isempty(treePath)
      end

      % Display the (possibly-modified) context menu
      jmenu.show(jtree, clickX, clickY);
      jmenu.repaint;
   end
end

% Remove the extra context menu item after display
function removeItem(hObj,eventData,jmenu,item)
   jmenu.remove(item);
end

% Menu items callbacks must receive at least 2 args:
% hObject and eventData – user-defined args follow after these two
function myfunc1(hObject, eventData)
   % ... 

function myFunc2(hObject, eventData, myData1, myData2)
    % ... 

uitree node-specific context-menu

uitree node-specific context-menu
(note the HTML formatting of menu items)

If the issue of undocumented Matlab interests you, please review the list of future post topics and let me know if you have any preference, or would like to see some other (unlisted) topics.

Yair

Bookmark and Share

Setting line position in an edit-box uicontrol

Thursday, March 26th, 2009

I often see requests in the Matlab forum (CSSM) regarding modifying some uicontrol property that is not exposed by Matlab. Since all Matlab uicontrols are based on underlying Java Swing controls, accessing these features is possible via their Java control peers.

In this post I will give the example of setting the caret (cursor) position in a multi-line edit-box. Apparently, whenever the string contents of such a uicontrol is modified, Matlab automatically sets the caret position on the first character of the first line. It is often requested to place the caret on the last line, so that the last line scrolls into view, rather than the first line. This is the case, for example, if you wish to display an event log that keeps adding new entries at the bottom.

The first step in accessing the underlying Java control is to download my FindJObj submission on the File Exchange. FindJObj searches down the window frame hierarchy until it finds a Java control with the exact position and size of the requested Matlab HG handle (FindJObj has lots of other goodies which will be described in another post).

Once we have the Java peer reference handle (in our case, a UIScrollPane object), we select its internal edit-box control (an object of class com.mathworks.hg.peer.EditTextPeer$hgTextEditMultiline) and use its setCaretPosition() method to move the caret to the end of the text:

>> % Create the multi-line edit-box
>> str = {'multi','line','editbox'};
>> hEdit = uicontrol('style','edit','max',3,'string',str);

Editbox caret at top row (default)

Editbox caret at top row (default)

>> % Get the underlying Java control peer (a scroll-pane object)
>> jhEdit = findjobj(hEdit)
jhEdit =
	javahandle_withcallbacks.com.mathworks.hg.peer.utils.UIScrollPane

>> % Check that the scrollpane has a multiline edit control and 2 scrollbars
>> jhEdit.list
com.mathworks.hg.peer.utils.UIScrollPane[...]
 javax.swing.JViewport[...]
  com.mathworks.hg.peer.EditTextPeer$hgTextEditMultiline[...]
 com.mathworks.hg.peer.utils.UIScrollPane$1[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]
 com.mathworks.hg.peer.utils.UIScrollPane$2[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]
  com.sun.java.swing.plaf.windows.WindowsScrollBarUI$WindowsArrowButton[...]

>> % Get the scroll-pane's internal edit control
>> jEdit = jhEdit.getComponent(0).getComponent(0)
jEdit =
com.mathworks.hg.peer.EditTextPeer$hgTextEditMultiline[...]

>> % Now move the caret position to the end of the text
>> jEdit.setCaretPosition(jEdit.getDocument.getLength);

Editbox caret at bottom (via Java)

Editbox caret at bottom (via Java)

Setting the caret position works, but is actually better done on the EDT using awtinvoke or javaMethodEDT. This will be explained in a separate EDT-specific post (see my TODO list).

Bookmark and Share

Setting system tray icons

Tuesday, March 24th, 2009

Java 1.6, included in Matlab releases since Matlab 7.5 (R2007b), enables programmatic access to system tray icons on such systems that support this functionality (Windows, Linux and possibly others).  If the SystemTray object indicates that it isSupported(), then a TrayIcon can be added, along with an associated tooltip and popup menu:

sysTray = java.awt.SystemTray.getSystemTray;
if (sysTray.isSupported)
   myIcon = fullfile(matlabroot,'/toolbox/matlab/icons/matlabicon.gif');
   iconImage = java.awt.Toolkit.getDefaultToolkit.createImage(myIcon);
   trayIcon = java.awt.TrayIcon(iconImage, 'initial tooltip');
   trayIcon.setToolTip('click this icon for applicative context menu');
end

sample system tray icon

sample system tray icon

The icon image can be made to automatically resize to the system-tray dimensions, using the trayIcon.setImageAutoSize(true) method (by default the icon image will maintain its original size, getting cropped or appearing small as the case may be).

Of course, after initial setup, all the sys-tray icon’s properties (icon image, popup, tooltip etc.) can be modified with convenient set methods (setImage(), setPopupMenu(), setTooltip()) or via Matlab’s set() function.

Icon popup menus are very similar in concept to Matlab uicontextmenus. Unfortunately, they need to be programmed separately since Java does not accept uicontextmenu handles. This is actually quite easy, as the following code snippet shows:

% Prepare the context menu
menuItem1 = java.awt.MenuItem('action #1');
menuItem2 = java.awt.MenuItem('action #2');
menuItem3 = java.awt.MenuItem('action #3');

% Set the menu items' callbacks
set(menuItem1,'ActionPerformedCallback',@myFunc1);
set(menuItem2,'ActionPerformedCallback',{@myfunc2,data1,data2});
set(menuItem3,'ActionPerformedCallback','disp(''action #3...'')');

% Disable one of the menu items
menuItem2.setEnabled(0);        % or: set(menuItem2,'Enabled','off');

% Add all menu items to the context menu (with internal separator)
jmenu = java.awt.PopupMenu;
jmenu.add(menuItem1);
jmenu.add(menuItem2);
jmenu.addSeparator;
jmenu.add(menuItem3);

% Finally, attach the context menu to the icon
trayIcon.setPopupMenu(jmenu);    % or: set(trayIcon,'PopupMenu',jmenu);

Tray icon context (right-click) menu

Tray icon context (right-click) menu

Unfortunately, neither the icon tooltip nor its popup menu supports HTML. The reason is that SystemTray is actually not part of Swing at all. The system-tray functionality resides in the java.awt package, and does not inherit javax.swing.JLabel’s (and Matlab uicontrols) support for HTML.

I have created a utility function called SYSTRAY, which is a convenience function that facilitates the setup and update of system tray icons. SYSTRAY (with source code) can be downloaded from the File Exchange.

In a separate post, I shall detail how informational pop-up messages can be attached to system-tray icons. This requires a bit of Java-hacking, so is beyond the scope of a single blog post.

Please note the new TODO page, which details my future posts. I would be happy to hear your requests for new topics, or telling me which topics you’d like to see earlier than others.

Addendum (May 15, 2009): A kind reader today left a comment on another post of this blog with a solution for some reported Java exceptions when using systray in Matlab R2008b onward.

Bookmark and Share

HTML support in Matlab uicomponents

Saturday, March 21st, 2009

A common feature of Java Swing components is their acceptance of HTML (and CSS) for any of their JLabels. Since all Matlab uicontrols are based on Swing-derived components (an undocumented aspect), this Swing feature automatically applies to Matlab uicontrol as well. Whatever can be formatted in HTML (font, color, size, …) is inherently available in Matlab controls. Note that HTML tags do not need to be closed (<tag>…</tag>), although it is good practice to close them properly.

For example, let us create a multi-colored Matlab listbox:

uicontrol('Style','list', 'Position',[10,10,70,70], 'String', ...
{'<HTML><FONT color="red">Hello</Font></html>', 'world', ...
 '<html><font style="font-family:impact;color:green"><i>What a', ...
 '<Html><FONT color="blue" face="Comic Sans MS">nice day!</font>'});

Listbox with HTML'ed items

Listbox with HTML colored items

Menus and tooltips can also be customized in a similar fashion:

uicontrol('Style','popup', 'Position',[10,10,150,100], 'String', ...
{'<HTML><BODY bgcolor="green">green background</BODY></HTML>', ...
 '<HTML><FONT color="red" size="+2">Large red font</FONT></HTML>', ...
 '<HTML><BODY bgcolor="#FF00FF"><PRE>fixed-width font'});

Drop-down (popup uicontrol) with HTML'ed items

Drop-down (popup uicontrol) with HTML'ed items

Multi-line HTML'ed tooltip

Multi-line HTML'ed tooltip

Figure main menu modified with HTML items

Figure main menu modified with HTML items

Bookmark and Share

Changing Matlab’s Command Window colors

Thursday, March 19th, 2009

Yesterday I received an email asking if it was possible to programmatically change Matlab’s Command Window foreground and background colors. This is possible from the File/Preferences window, but requires interactive user actions. The question was whether it was possible to do so programmatically, in order to place the necessary commands in some script (for example, startup.m). This is important, for example, when we have two Matlab applications open at the same time and wish to visually distinguish between them.

After getting this email, one in a long list of similar questions I tackled over the past few years regarding Matlab’s undocumented/unsupported/hidden features, I decided it was time to start a blog on this broad subject. I do not know if there is any interest in the subject of this blog, so I would be extremely happy to hear feedback.

Changing Command-Window colors can be done programmatically in two manners: The first is by modifying system preferences. The issue of programmatic access to system preferences is detailed in another dedicated post. Here is the bottom line regarding the colors:

% Don't use system color
com.mathworks.services.Prefs.setBooleanPref('ColorsUseSystem',0);
 
% Use specified colors for foreground/background (instead of the default black on white)
com.mathworks.services.Prefs.setColorPref('ColorsBackground',java.awt.Color.yellow);
com.mathworks.services.ColorPrefs.notifyColorListeners('ColorsBackground');

and similarly for the foreground color, whose property name is called ‘ColorsText’. Note that colors can be specified in several alternative manners (see below).

This affects all Matlab text panes (Command Window, Command History, Workspace Browser etc.), for this and all future Matlab sessions. If you only wish to set the colors in the command window and not in all the other Matlab text panes, or if you only wish to modify this session, then forget the prefs method. Instead, simply use the following short code snippet (you may need to tweak it for particular Matlab versions) to change the colors of only the command text pane (that’s a Swing JTextArea, for those of you who are java-savvy).

cmdWinDoc = com.mathworks.mde.cmdwin.CmdWinDocument.getInstance;
listeners = cmdWinDoc.getDocumentListeners;
jTextArea = listeners(3);

Note: a safer way would be to loop on listeners until finding a JTextArea instance, since it is not assured to be the 3rd item in the listeners array.

Now you can set the colors (which apply immediately) using several alternative ways:

jTextArea.setBackground(java.awt.Color.yellow);
jTextArea.setBackground(java.awt.Color(1,1,0));
set(jTextArea,'Background','yellow');
set(jTextArea,'Background',[1,1,0]);

You can do the same with the ‘Foreground’ property.

Please let me know what you think of this post, or of this blog in general. Your feedback, as well as additional questions and suggestions are most welcome.

Yair Altman

altmany at gmail dot com

Bookmark and Share