Skip to main content

JTrayIcon update

Posted by alexfromsun on February 14, 2008 at 11:12 AM PST

JDK 6 introduced plenty of cool desktop features, like famous gray-rect fix or advanced drag and drop support,

all of them made java much closer to the desktop application market and I am very exited about them.

There have been many blogs how useful new features are,
and I don't want to repeat them. Among all successful and well-designed features there is one which actually has not been completed,

I am speaking about JTrayIcon.

Swing and AWT team work together (by the way, AWT team seats in 10 steps from my room), AWT is responsible for the "native" parts of the code, like topLevel frames, event processing etc,

when Swing team if focused on their lightWeight components. Actually it is AWT who isolates Swing from any native code and makes Swing cross-platform.

It wasn't a surprise that TrayIcon feature came to AWT team, and they implemented it perfectly - it works well and it is supported for various Windows, Linux and Solaris systems.

They made it customizable by providing mouse event handling support and a special method setPopupMenu() which takes java.awt.Popup (which is quite old-fashioned but remember it was the AWT feature)

JPopupMenu doesn't extend awt.Popup but it looked like it would be easy to attach JPopupMenu to the trayIcon with help of MouseListener, but it was not the case.

The fact is that there is no way (or at least easy one) to correctly attach JPopupMenu to a TrayIcon.

It means that you can't set an icon for a TrayIcon's menuItem or make it look according to the current Look and Feel and, from my point of view, it makes TrayIcon much less useful than it could be.

It is our fault, sorry about that. Now let's see how we can solve this problem.

To make JTrayIcon possible I fixed three particular bugs for the next JDK 6 update release (the famous update #10, aka 6uN) and for the JDK 7 beta

ClassCastException in Swing with TrayIcon

If JPopupMenu is shown from a trayIcon's mouseListener an ugly ClassCastException was thrown in some cases.

Keyboard navigation problems and highlighting of the first item

If popup menu lost focus it stopped processing keyboard events in some cases, the first item a popup was highlighed by default
which is very different from any other GUI I've seen

Swing Popups should overlap taskbar

Finally it was impossible to implement a good looking trayicon's popup menu, because Swing popups couldn't overlap taskbar

Having integrated all three fixes I managed to write the JXTrayIcon class
which extends java.awt.TrayIcon and contains
setJPopuMenu(JPopupMenu) method

Here is how JXTrayIcon looks when you click on it, with the current JDK 6

JXTrayIcon with the current JDK 6

the same code is run under JDK 6 update 10 or JDK 7

JXTrayIcon with JDK 6u10 or JDK 7

The red quarter circle icon is owned by the JXTrayIcon's test.
Note that the first item is not highlighted by default when the popup menu is shown,

if you move the mouse over it or press the arrow button on your keyboard, the proper item will be highlighted as with a system popup menu.

I encourage you to download the JDK 6u10 early bits and try out the JXTrayIcon demo

The runnable jar file is also available for your convenience

I particularly interested how it works in different environments,

any comments and suggestions are welcome as usual

Thank you and sorry this took so long.I hope it was worth the wait.

alexp

Related Topics >>

Comments

Hello Alex I tried your JXTrayIcon class on windows XP, JDK 1.6.0_13. It works great for the most part but sometimes, the tray icon just dissappears! I can see the java process in the task manager, so the app is alive (logs are being updated by some threads). Can you shed some light on what could be going on? Thanks!

Hello Bernard The recent news about JXTrayIcon on Linux are not so good, unfortunately it doesn't work there out-of-the-box Michael Bien found the best workaround so far: https://fishfarm.dev.java.net/source/browse/fishfarm/trunk/FishFarm/src/... I hope we identify all the problems with JXTrayIcon now, I'll nudge the responsible engineer of the bug 6440297 Thanks for the information alexp

Thanks for this, I'm looking forward to testing on Ubuntu real soon! Unfortunately, I'm disappointed with jTrayIcon on Linux. I've written a couple of small system tray applications on Windows and they look/work beautifully. I then ran them on Ubuntu and to my horror the tray icon hover text does not show multiple lines. It turns out it's related to this bug: 6440297 This is really frustrating as (apparently) it's been a bug that has been around for a while (not just in jTrayIcon) but in labels too. Alex, do you have any friends over in java.awt.Graphics that you can nudge? I believe that not only should escape characters be observed, but so too should System.getProperty( "line.separator" ). Thanks for the blog, Bernard.

Hi Alex. Sorry I didn't make myself clear. What I'm questioning is that I'm not seeing a big advantage of JXTrayIcon, given that it's using a hidden undecorated JDialog as the "invoker" for the popup menu. Your design for JXTrayIcon creates an essentially hidden JDialog, which is set to visible before showing the JPopupMenu. You of course do this to get around the (shall we say broken) implementation of Swing's TrayIcon class with regards to displaying menus. In my mind, this creates a level of unnecessary complexity since the JDialog could, in essence, be used to display a menu of sorts. One could simply create the JDialog with a Box or GridLayout and add AbstractButtons (JButton or JMenuItem) to it to emulate a traditional JPopupMenu. The Dialog itself becomes the menu. The only nicety that JPopupMenu has is automatically going invisible when it loses focus. This effect could of course be replicated by using a WindowFocusListener on the JDialog. So, in summary: 1) Create the TrayIcon 2) Create a JDialog, undecorated=true 3) Add various menu buttons to the JDialog 4) Add a WindowFocusListener to the JDialog to setVisible(false) when focus is lost 5) Add a MouseListener to TrayIcon to capture the appropriate MouseEvent 6) Display the JDialog at the appropriate mouse click location This just seems like a cleaner way to work around the problems associated with TrayIcon and menus. Or, am I missing something? Maybe I'm missing that people are attempting to attach an already existing JPopupMenu (from another Component) to the TrayIcon and don't want to bother creating a separate dialog to be used as a menu? Adam Taft

Hello Adamtaft I have read your message a few times and must admit that I don't get your question What is the issue you are talking about? and what "make the JDialog your menu" means? Thanks alexp

I guess I'm missing something here... If you're creating a "hidden" JDialog (as JXTrayIcon does), just for the sake of displaying a JPopupMenu, why not just make the JDialog your menu. I mean, I guess if you're really into using JMenuItem instead of JButton, then this hack seems OK. Until this issue is completely resolved, though, I think simply displaying an always-on-top JDialog seems cleaner (to me).

Ya, I think you're right. Having JPopupMenu available as the popup for a TrayIcon makes sense. In particular, my suggestion for using JDialog wouldn't obviously allow for submenus. My parenthesized suggestion that JMenuItem could be directly added to JDialog was more off-the-cuff. You're right, that's absolutely not reasonable. However, your implication that interaction with the system tray involves anything other than the mouse is also a bit far fetched as well. It would be an extremely rare type of individual who interacts with his system tray via keyboard. ;) The updates you have done in 6uN though (particularly the ability for JPopupMenu to display above the task bar) might convince me that your JXIconTray hack is the way to go. So that's cool. Good job getting those changes in. Thanks for the dialog. Adam

Hello Adam I doubt that adding menu buttons directly to the JDialog would work the same way as popupMenu does If it works well for you, please send me your code (don't forget to test how it works with the arrow keys button) Thanks alexp

I tested again with Java SE update N build 8 and 12 on ubuntu 7.10 and it worked this time. Good work with the bug fixes!

The only glitch I noticed is that the tray Popup is listed as regular window in the task bar - probably another linux related bug.

Hello Kirill

Actually it was me who Artem consulted with before he wrote that blog
(the jpopup.setInvoker(jpopup) hack)
I see no contradictions here, take the screenshot from the Artem's blog
you can see there that popup menu doesn't overlap the taskbar like all system popups do
and ClassCastException was somewhere under the table, just have a look at the comments

Thanks for the information about the Windows 7

alexp

Alex, there seems to be contradictory statements about JPopupMenu on tray icons coming from the Swing team. What about Artem's post from 2006 that shows how to do it? And it looks like you'll have yet another scenario to check in the next Windows version. At least the first milestone of Windows 7 changed the way the hidden tray icons are operated.

Hello cowwoc

Does it happen because Java and Thunderbird tray icons
are placed on the left side of your tray area
and they have enough room to show their popups
when the other popups can't be shown at right side of the click ?

Thanks
alexp

Under Vista 64-bit I notice that the pop-up menu always displays to the left of the tray icon while both the Java pop-up and Thunderbird one display to the right.

Is there some OS-level property we should be reading which indicates which direction the menus should pop up and we're forgetting to do this?

Thanks for working on this Alex!

oops, sorry for no formatting - my fault...

Hi!
thanx for article, already had same problem, but...

Method, described by Artem, didn't worked on my Linux box (menu showed but never hided until you implement some additions to listener, but it is not a way).

I haven't already tested, but it seems to me that JDIC project already solved this problem. If you download JDIC source package (from jdic.dev.java.net) you may look at WinTrayIconService and GnomeTrayIconService classes, both of them have method setPopupMenu with popup menu listeners, and this listeners do all the job (and looks similar enough) of handling JPopupMenu. The only problem i see - i can't understand using of sun.* packages and any need of them.

May be we can unite our forces to implement TrayIcon implementation that will rule? :)

PS: another problem i see - KDE supports transparency for System Tray, and the only Java tray implementation that supports it - Qt Jambi (http://doc.trolltech.com/qtjambi-4.3.2_01/com/trolltech/qt/qtjambi-syste...)

Few feedback using JXTrayIcon + Kubuntu 7.10 + KDE+ Sun JRE 1.6.0 u5:

* There's still the bug that I encountered in JDIC : the popup is only flashing (except if clicking on the right side of the tray icon) https://jdic.dev.java.net/issues/show_bug.cgi?id=401 To avoid the popup closing, you have to release the mouse over it only
A working workaround (for jdic) is given here : https://jdic.dev.java.net/issues/show_bug.cgi?id=155

* Sometimes, when right clicking on the tray icon, then releasing the click over an item fire the action

* With jdic, a cool feature was the fact, under Linux only however to be allowed to add HTML code in trayicon tooltips and ballon (we displayed images and rich text), is there a way to achieve this in Mustang ?

* It's a pity we still have the gray rectangle behind the tray under Linux. Tip: use shadows to make it less... gray

* Thanks for this article and JXTrayIcon, it works like a charm and saved me a lot of time

One other thing: you probably meant to name the method getJPopupMenu, not getJPopuMenu

This is cool. I had decided to use JDIC's TrayIcon, but (a) it requires native libraries that don't ship with the JRE, and (b) Gnome support is broken in 0.9.4, there is a function missing out of libtray.so, and no one has responded to my forum post inquiring about the problem (probably ought to go file a bug report too)... However, I use a third-party L&F in my app, making the awt Popup Menu unusable. So... this blog entry was a godsend for me!

But I notice that the ClassCastException still occurs in some cases. It only happens if I right-click the tray icon while the menu is visible, and it doesn't happen consistently. I grabbed the JXTrayIcon source and am trying to figure out if there's a way to fix the problem.

Hello bflorat

Thanks for the informative comments !
We are working on making trayIcons more useful

Thanks
alexp

Hello Sjsobol

I also found this one more mean ClassCastException
bug 6690791 will be fixed soon

getJPopuMenu is another good catch, fixed

Thank you!
alexp

Correction: It doesn't pop up on the correct screen some times. It seems the first popup is always on the wrong screen. Other times it (sort of) works. Often the menu will appear and then immediately disappear. Not sure if it's reliable enough to use in production at this point. Which is a shame because the AWT popups look awful. Also, if the popup menu is open and you try to cancel/close it by clicking on another application in the OS, it won't close like a normal popup menu. It just stays open until you either click on the popup menu or the Java application window (which is impossible if the TrayIcon is the only thing showing). Feels really bad.

This doesn't appear to work correctly with multiple screens. My system tray is not on the primary display and when the menu pops up it's on the wrong screen. At least on Ubuntu (using TwinView), I haven't tried Windows and OS X yet but I wonder if they will have the same problem if the tray area is not on the primary display.

Thanks for filing the bug. And... Great job on the JXTrayIcon class! I was getting the exception on Windows too, but I must not have been using the correct JRE. Yesterday I installed the latest 6uN beta on one of my WIndows PCs and I do not get the exception. Now, can you tell us what the timeframe is for release of 6uN? Is there anything more specific than "later this year"? Thanks, Steve

Hello Steve

Thank you very much
sorry but I don't have any updates about 6uN timeframe

Best regards
alexp

Hello everybody

to ericzenviva
with Swing menu it is certainly possible to set the bold font for a menuItem,
see JMenuItem.setFont()

to mbien
This exception is thrown by AWT, I think it is also fixed for 6u10,
see bug 6438179

to lurk_3245
The idea behind JXTrayIcon is to provide basic functionality
it shouldn't be a problem to customize it for particular application

to kermitas
I didn't know about this issue with awt popups
I agree with you - it would look really strange for programms with animation

Thank you !
alexp

Great work!
You also solved my problem described here http://groups.google.pl/group/comp.lang.java.gui/browse_thread/thread/222ab346e419a249/79563f7351f9ec29?lnk=st .
The problem was about tray menu that blocks event dispath thread (EDT) while it was shown. It is not professional when program contains progress bars, animations, ect..

Observations from Windows: 1. Some native apps display a different menu on right and left click, some display the same menu on both, and some display only one menu on right click. 2. Some applications display one (anywhere in the menu) item highlighted/bold. This item is the default item, and is executed with a double click on the icon, rather than selection in the menu. Might be a good idea to take this into account, combined with the possibility og having two different menu's, from point 1.

I get this Exception if I try the tray.jar under ubuntu 7.10. Exception in thread "AWT-EventQueue-0" java.lang.UnsupportedOperationException at java.awt.TrayIcon.(TrayIcon.java:105) at java.awt.TrayIcon.(TrayIcon.java:129) at org.jdesktop.swinghelper.tray.JXTrayIcon.(Unknown Source) at org.jdesktop.swinghelper.tray.JXTrayIcon.createGui(Unknown Source) at org.jdesktop.swinghelper.tray.JXTrayIcon.access$200(Unknown Source) at org.jdesktop.swinghelper.tray.JXTrayIcon$3.run(Unknown Source) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:597) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160) at java.awt.EventDispatchThread.run(EventDispatchThread.java:121) java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)

works fine on XP with update N installed very good!

I am looking forward to see the JXTrayIcon in the next SwingX release ;-)

We've implemented the tray icon support into our latest Yakkle release (www.yakkle.com) and since our application needs to inform our users about incoming instant messages or incoming emails or contacts coming online, it has worked like a champ! Is there a way to "bold" one of the entries in the tray popup menu? I notice that it seems the "default" operation is bolded for Windows apps that use tray icons and I'd like to have that behavior here.

Hello Everybody

The bug 6690791 is fixed for JDK 6u10
no more ClassCastException is thrown

Thanks
alexp