/***
* Windows Tray Icon
* -----------------
*
* Written by Jan Struyf
*
* jan.struyf@cs.kuleuven.ac.be
* http://jeans.studentenweb.org/java/trayicon/trayicon.html
*
* Please mail me if you
* - 've found bugs
* - like this program
* - don't like a particular feature
* - would like something to be modified
*
* I always give it my best shot to make a program useful and solid, but
* remeber that there is absolutely no warranty for using this program as
* stated in the following terms:
*
* THERE IS NO WARRANTY FOR THIS PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
* LAW. THE COPYRIGHT HOLDER AND/OR OTHER PARTIES WHO MAY HAVE MODIFIED THE
* PROGRAM, PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
* TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
* PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
* REPAIR OR CORRECTION.
*
* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ANY COPYRIGHT HOLDER,
* OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM,
* BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
* PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
* INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
* PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
* PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* May the Force be with you... Just compile it & use it!
*/
package com.jeans.trayicon;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;
import java.util.Enumeration;
import java.util.Vector;
/**
* WindowsTrayIcon A Java Implementation for showing icons in the Windows System Tray Written by Jan Struyf
* (jan.struyf@cs.kuleuven.ac.be) (http://ace.ulyssis.org/~jeans) Instantiate this class for each icon This file comes
* with native code in TRAYICON.DLL The DLL should go in C:/WINDOWS/SYSTEM or in your current directory
*/
public class WindowsTrayIcon
{
public final static String TRAY_VERSION = "1.7.9b";
private static TrayIconKeeper m_Keeper;
private static TrayDummyComponent m_Dummy;
private static MouseListener m_MouseHook;
private static Window m_CurrentWindow;
/*******************************************************************************************************************
* * Initialisation / Termination * *
******************************************************************************************************************/
/**
* Init native library - call this method in the main() method of your app Param appName = the title for the hidden
* window Each app has it's own hidden window that receives the mouse/menu messages for it's Tray Icons The window
* title is used by sendWindowsMessage() and isRunning() to identify an app
*/
public static void initTrayIcon( final String appName )
{
WindowsTrayIcon.initTrayIcon( appName, new WindowsTrayIcon() );
}
/**
* Free all native resources - call this method before System.exit()
*/
public static void cleanUp()
{
if ( WindowsTrayIcon.m_Keeper != null )
{
WindowsTrayIcon.m_Keeper.doNotify();
WindowsTrayIcon.m_Keeper = null;
}
WindowsTrayIcon.termTrayIcon();
}
/*******************************************************************************************************************
* * Constructor * *
******************************************************************************************************************/
/**
* Construct a new Tray Icon Using a Java Image - This can be loaded from a 16x16 GIF or JPG file Param image 16x16
* icon - make sure it's loaded in memory - use MediaTracker Param w the icon width - eg. 16 Param h the icon height -
* eg. 16 Exception TrayIconException - if something goes wrong :O( - Too many icons allocated - Error initializing
* native code DLL - Error setting up Windows notify procedure - Error loading icon image Exception
* InterruptedException - if the thread loading the image is interrupted
*/
public WindowsTrayIcon( final Image image, final int w, final int h )
throws TrayIconException, InterruptedException
{
// Allocate new id for icon (native routine)
this.m_ID = WindowsTrayIcon.getFreeId();
if ( this.m_ID == WindowsTrayIcon.TOOMANYICONS )
{
throw new TrayIconException( "Too many icons allocated" );
}
if ( this.m_ID == WindowsTrayIcon.DLLNOTFOUND )
{
throw new TrayIconException( "Error initializing native code DLL" );
}
if ( this.m_ID == WindowsTrayIcon.NOTIFYPROCERR )
{
throw new TrayIconException( "Error setting up Windows notify procedure" );
}
// Store image data and size
this.setImage( image, w, h );
}
/*******************************************************************************************************************
* * Methods * *
******************************************************************************************************************/
/**
* Change this icon's Image Using a Java Image - This can be loaded from a 16x16 GIF or JPG file Param image 16x16
* icon - make sure it's loaded in memory - use MediaTracker Param w the icon width Param h the icon height
* Exception TrayIconException - if something goes wrong :O( - Error loading icon image Exception
* InterruptedException - if the thread loading the image is interrupted
*/
public void setImage( final Image image, final int w, final int h )
throws TrayIconException, InterruptedException
{
try
{
// Collect pixel data in array
int[] pixels = new int[ w * h ];
PixelGrabber pg = new PixelGrabber( image, 0, 0, w, h, pixels, 0, w );
pg.grabPixels();
if ( ( pg.getStatus() & ImageObserver.ABORT ) != 0 )
{
this.freeIcon();
throw new TrayIconException( "Error loading icon image" );
}
// Send image data to the native library
WindowsTrayIcon.setIconData( this.m_ID, w, h, pixels );
}
catch ( InterruptedException ex )
{
this.freeIcon();
throw ex;
}
catch ( NullPointerException ex )
{
this.freeIcon();
throw ex;
}
}
/**
* Show/Hide this icon in the Windows System Tray Param status true = show, false = hide
*/
public void setVisible( final boolean status )
{
WindowsTrayIcon.showIcon( this.m_ID, status );
}
/**
* Test if this icon is currently visible in the Windows System Tray Returns true if visible
*/
public boolean isVisible()
{
return WindowsTrayIcon.testVisible( this.m_ID ) == 1;
}
/**
* Changes the text for the ToolTip of this icon The ToolTip is displayed when the user mouses over the icon Param
* tip = the new text for the ToolTip
*/
public void setToolTipText( final String tip )
{
WindowsTrayIcon.setToolTip( this.m_ID, tip );
}
/**
* Display a balloon message for the icon
*/
public final static int BALLOON_NONE = 0;
public final static int BALLOON_INFO = 1;
public final static int BALLOON_WARNING = 2;
public final static int BALLOON_ERROR = 3;
public final static int BALLOON_NOSOUND = 0x10;
public void showBalloon( final String msg, final String title, final int timeout, final int flags )
throws TrayIconException
{
if ( WindowsTrayIcon.showBalloon( this.m_ID, msg, title, timeout, flags ) == 0 )
{
throw new TrayIconException( "Error showing Balloon message" );
}
}
/**
* Add an ActionLister to this icon Just like with java.awt.Button or javax.swing.JButton Param listener = your
* listener
*/
public void addActionListener( final ActionListener listener )
{
if ( this.m_ActList == null )
{
this.m_ActList = new Vector();
WindowsTrayIcon.clickEnable( this, this.m_ID, true );
}
this.m_ActList.addElement( listener );
}
public void removeActionListener( final ActionListener listener )
{
this.m_ActList.removeElement( listener );
}
public void addMouseListener( final MouseListener listener )
{
if ( this.m_MouseList == null )
{
this.m_MouseList = new Vector();
WindowsTrayIcon.clickEnable( this, this.m_ID, true );
}
this.m_MouseList.addElement( listener );
}
public void removeMouseListener( final MouseListener listener )
{
this.m_MouseList.removeElement( listener );
}
public void addBalloonListener( final TrayBalloonListener listener )
{
if ( this.m_BalloonList == null )
{
this.m_BalloonList = new Vector();
WindowsTrayIcon.clickEnable( this, this.m_ID, true );
}
this.m_BalloonList.addElement( listener );
}
public void removeBalloonListener( final TrayBalloonListener listener )
{
this.m_BalloonList.removeElement( listener );
}
/**
* Set new popup menu The popup menu is displayed when the user right clicks the icon See class TrayIconPopup,
* TrayIconPopupSimpleItem, .. Param popup = the popup menu
*/
public void setPopup( final TrayIconPopup popup )
{
if ( popup == null )
{
this.m_Popup = null;
WindowsTrayIcon.initPopup( this.m_ID, -1 );
}
else
{
if ( this.m_Popup == null )
{
WindowsTrayIcon.clickEnable( this, this.m_ID, true );
}
int levels = popup.getNbLevels();
WindowsTrayIcon.initPopup( this.m_ID, levels );
popup.setTrayIcon( this, this.m_ID, -1 );
this.m_Popup = popup;
}
}
/**
* Free all native resources for this icon On exit use cleanUp()
*/
public void freeIcon()
{
WindowsTrayIcon.clickEnable( this, this.m_ID, false );
WindowsTrayIcon.freeIcon( this.m_ID );
}
public static native void setAlwaysOnTop( Component wnd, boolean onTop );
public final static int UNICODE_CONV_BALLOON = 2;
public final static int UNICODE_CONV_SUPPORT = 1;
public static native void enableUnicodeConversion( int component, boolean enable );
public static native boolean hasUnicodeConversion( int component );
public static native boolean supportsBalloonMessages();
/**
* Return error code from native library - use for debugging
*/
public static native int getLastError();
// No error occured since the last call to getLastError()
// There are a lot errors declared but they are only there for debug reasons
public static final int NOERR = 0;
// The ActionListeners of the icon need to be notified when the user clicks it
// In the Windows API this is accomplished using a Notify Procedure
public static final int NOTIFYPROCERR = -1;
// The DLL has a fixed data structure that can contain up to 100 icons
// Hope that's enough for you
public static final int TOOMANYICONS = -2;
// This happens when C++ is out of memory
public static final int NOTENOUGHMEM = -3;
// Each icon has one unique id number
public static final int WRONGICONID = -4;
// The native code can't locate the DLL
// Try moving it to C:/WINDOWS/SYSTEM or something like that
public static final int DLLNOTFOUND = -5;
// Invocation code can't find your Java VM during callback
public static final int NOVMS = -6;
// Invocation API can't attach native thread to your Java VM
public static final int ERRTHREAD = -7;
// Error in lookup of the notifyListeners() method in this class
// The DLL has to do this when the user clicks one of your icons
public static final int METHODID = -8;
// Not really an error..
// This happens when the user clicks an icon that has no ActionListener yet
public static final int NOLISTENER = -9;
// One of the Invocation JNI Functions returned an error
public static final int JNIERR = -10;
// Error showing balloon
public static final int ERRORBALLOON = -18;
/*******************************************************************************************************************
* * Windows messaging code for detecting previous instance * *
******************************************************************************************************************/
/**
* Checks if there's an instance with hidden window title = appName running Can be used to detect that another
* instance of your app is already running (so exit..) Param appName = the title of the hidden window to search for
*/
public static native boolean isRunning( String appName );
/**
* Send a message to another app (message can contain an integer) Can be used to detect that another instance of
* your app is already running That instance can for example restore it's window after it receives the windows
* message - see demo app for more info Param appName = the title of the hidden window to search for Param message =
* the integer message to send (only native int size supported)
*/
public static native int sendWindowsMessage( String appName, int message );
/**
* Set callback method for receiving windows messages See sendWindowsMessage() for more information or take a look
* at the demo app Param callback = the callback method for this app
*/
public static void setWindowsMessageCallback( final TrayIconCallback callback )
{
WindowsTrayIcon.m_WMessageCallback = callback;
}
/**
* Keep TrayIcon alive (make sure application does not exit)
*/
public static void keepAlive()
{
if ( WindowsTrayIcon.m_Keeper == null )
{
WindowsTrayIcon.m_Keeper = new TrayIconKeeper();
WindowsTrayIcon.m_Keeper.start();
}
}
public final static int FLASHW_STOP = 0;
public final static int FLASHW_CAPTION = 1;
public final static int FLASHW_TRAY = 2;
public final static int FLASHW_ALL = WindowsTrayIcon.FLASHW_CAPTION | WindowsTrayIcon.FLASHW_TRAY;
public final static int FLASHW_TIMER = 4;
public final static int FLASHW_TIMERNOFG = 12;
public static void flashWindow( final Frame wnd )
throws TrayIconException
{
WindowsTrayIcon.flashWindow( wnd, WindowsTrayIcon.FLASHW_ALL | WindowsTrayIcon.FLASHW_TIMERNOFG, 0, 0 );
}
public static void flashWindow( final Frame wnd, final int flags, final int count, final int timeout )
throws TrayIconException
{
WindowsTrayIcon.flashWindow( wnd.getTitle(), flags, count, timeout );
}
public static void flashWindow( final String title, final int flags, final int count, final int timeout )
throws TrayIconException
{
if ( !WindowsTrayIcon.flashWindowImpl( title, flags, count, timeout ) )
{
throw new TrayIconException( "Flash window not supported" );
}
}
public static native boolean flashWindowImpl( String title, int flags, int count, int timeout );
public static void setCurrentWindow( final Window wnd )
{
WindowsTrayIcon.m_CurrentWindow = wnd;
}
public static native String getWindowsVersionString();
public static native int getWindowsVersion();
public final static int WIN_VER_UNKNOWN = 0;
public final static int WIN_VER_WIN32 = 1;
public final static int WIN_VER_95 = 2;
public final static int WIN_VER_98 = 3;
public final static int WIN_VER_ME = 4;
public final static int WIN_VER_NT = 5;
public final static int WIN_VER_2K = 6;
public final static int WIN_VER_XP = 7;
public final static int WIN_VER_NET = 8;
public static boolean supportsBallonInfo()
{
int version = WindowsTrayIcon.getWindowsVersion();
return version >= WindowsTrayIcon.WIN_VER_2K;
}
/*******************************************************************************************************************
* * Next section is for inter use only -- or for hackers :O) * *
******************************************************************************************************************/
// Constructor
private WindowsTrayIcon()
{
}
// Each icon has a unique id ranging from 0..99
private int m_ID;
// Each icon can have a popup menu - activated when user right clicks the icon
private TrayIconPopup m_Popup;
// Each icon can have any number of ActionListeners - notified when user clicks (left/right) the icon
private Vector m_ActList, m_MouseList, m_BalloonList;
// Each application can have one WindowsMessageCallback - notified when another app uses sendWindowsMessage
private static TrayIconCallback m_WMessageCallback;
private final static int MOUSE_BTN_UP = 1;
private final static int MOUSE_BTN_DOUBLE = 2;
public static TrayDummyComponent getDummyComponent()
{
if ( WindowsTrayIcon.m_Dummy == null )
{
WindowsTrayIcon.m_Dummy = new TrayDummyComponent();
}
return WindowsTrayIcon.m_Dummy;
}
/**
* Private method called by native library when user clicks mouse button Param button = "Left" or "Right" or
* "Middle"
*/
private void notifyMouseListeners( final int button, final int mask, final int xp, final int yp )
{
int clicks = ( mask & WindowsTrayIcon.MOUSE_BTN_DOUBLE ) != 0 ? 2 : 1;
boolean up = ( mask & WindowsTrayIcon.MOUSE_BTN_UP ) != 0;
if ( this.m_ActList != null && clicks == 1 && up == false )
{
ActionEvent evt = null;
if ( button == 0 )
{
evt = new ActionEvent( this, 0, "Left" );
}
else if ( button == 1 )
{
evt = new ActionEvent( this, 0, "Right" );
}
else
{
evt = new ActionEvent( this, 0, "Middle" );
}
for ( Enumeration e = this.m_ActList.elements(); e.hasMoreElements(); )
{
ActionListener listener = (ActionListener) e.nextElement();
listener.actionPerformed( evt );
}
}
if ( this.m_MouseList != null )
{
int modifiers = 0;
if ( button == 0 )
{
modifiers |= InputEvent.BUTTON1_MASK;
}
else if ( button == 1 )
{
modifiers |= InputEvent.BUTTON2_MASK;
}
else
{
modifiers |= InputEvent.BUTTON3_MASK;
}
// (Component source, int id, long when, int modifiers, int x, int y, int clickCount, boolean popupTrigger)
MouseEvent evt =
new MouseEvent( WindowsTrayIcon.getDummyComponent(), 0, 0, modifiers, xp, yp, clicks, button == 1 );
for ( Enumeration e = this.m_MouseList.elements(); e.hasMoreElements(); )
{
MouseListener listener = (MouseListener) e.nextElement();
if ( up )
{
listener.mouseReleased( evt );
}
else
{
listener.mousePressed( evt );
}
}
}
}
/**
* Private method called by native library when something happens with the balloon message
*/
private void notifyBalloonListeners( final int mask )
{
if ( this.m_BalloonList != null )
{
TrayBalloonEvent evt = new TrayBalloonEvent( mask );
for ( Enumeration e = this.m_BalloonList.elements(); e.hasMoreElements(); )
{
TrayBalloonListener listener = (TrayBalloonListener) e.nextElement();
listener.balloonChanged( evt );
}
}
}
/**
* Private method called by native library when user selects popup menu item Param id = id of menu item (each menu
* item has unique id)
*/
private void notifyMenuListeners( final int id )
{
if ( this.m_Popup != null )
{
this.m_Popup.onSelected( id );
}
}
/**
* Private method called by native library when it receives a sendWindowsMessage event See sendWindowsMessage() for
* more information or take a look at the demo app Param lParam = parameter send along with windows message
*/
private static int callWindowsMessage( final int lParam )
{
if ( WindowsTrayIcon.m_WMessageCallback != null )
{
return WindowsTrayIcon.m_WMessageCallback.callback( lParam );
}
else
{
return 0;
}
}
private static void callMouseHook( final int xp, final int yp )
{
if ( WindowsTrayIcon.m_MouseHook != null )
{
MouseEvent evt = new MouseEvent( WindowsTrayIcon.getDummyComponent(), 0, 0, 0, xp, yp, 1, true );
WindowsTrayIcon.m_MouseHook.mousePressed( evt );
}
}
/**
* Modify property of menu item Param menuId = the id of the menu item Param what = which property to modify Param
* state = true property enabled
*/
void modifyPopup( final int menuId, final int what, final boolean state )
{
WindowsTrayIcon.modifyPopup( this.m_ID, menuId, what, state );
}
/**
* Init new popup menu - used by setPopup() Param id = the icon's id Param nblevels = the submenu depth of the new
* popup
*/
static native void initPopup( int id, int nblevels );
// Constants for builing a popup menu
// Used by subclasses of TrayIconPopupItem
final static int POPUP_TYPE_ITEM = 0; // Simple item
final static int POPUP_TYPE_SEPARATOR = 1; // Separator
final static int POPUP_TYPE_CHECKBOX = 2; // Checkbox item
final static int POPUP_TYPE_INIT_LEVEL = 3; // First item of submenu
final static int POPUP_TYPE_DONE_LEVEL = 4; // Last item of submenu
// Enable/Disable and friends
final static int POPUP_MODE_ENABLE = 1;
final static int POPUP_MODE_CHECK = 2;
final static int POPUP_MODE_DEFAULT = 4;
/**
* Add popup menu item - used by setTrayIcon() in subclasses of TrayIconPopupItem Param id = the icon's id Param
* level = the submenu level Param name = the name of the menu item Param type = POPUP_TYPE_ITEM or
* POPUP_TYPE_SEPARATOR or..
*/
static native int subPopup( int id, int level, String name, int type, int extra );
/**
* Modify menu item properties Param id = the icon's id Param menuId = the id of the menu item Param what = property
* to modify Param state = on/off
*/
private static native void modifyPopup( int id, int menuId, int what, boolean state );
/**
* Allocate a new id for icon - used in constructor
*/
private static native int getFreeId();
/**
* Set bitmap data for icon - used in constructor and setImage() Param id = the icon's id Param w, h = the images
* size Param pixels = the pixel array
*/
private static native void setIconData( int id, int w, int h, int pixels[] );
/**
* Make Tray Icon visible/invisible - used by setVisible() Param id = the icon's id Param hide = visible/invisible?
*/
private static native void showIcon( int id, boolean hide );
/**
* Test if Tray Icon is in the system tray - used by isVisible() Param id = the icon's id
*/
private static native int testVisible( int id );
/**
* Enable mouse/menu messages for icon - used by addActionListener() and setPopup() Param ico = the icons class
* (this) Param id = the icon's id Param enable = enable/disable mouse events?
*/
private static native void clickEnable( WindowsTrayIcon ico, int id, boolean enable );
/**
* Set tooltip - used by setToolTip(String tip) Param id = the icon's id Param tip = the new tooltip
*/
private static native void setToolTip( int id, String tip );
/**
* Free all native resources for this icon - used by freeIcon() Param id = the icon's id
*/
private static native void freeIcon( int id );
private static native void detectAllClicks( int id );
public static native void initJAWT();
public static native void initHook();
public static native void setMouseHookEnabled( int enable );
public static void setMouseClickHook( final MouseListener listener )
{
WindowsTrayIcon.m_MouseHook = listener;
WindowsTrayIcon.setMouseHookEnabled( listener == null ? 0 : 1 );
}
private static native void initTrayIcon( String appName, WindowsTrayIcon cls );
private static native int showBalloon( int id, String msg, String title, int timeout, int flags );
private static native void termTrayIcon();
public static Window getCurrentWindow()
{
return WindowsTrayIcon.m_CurrentWindow;
}
}