1 /*******************************************************************************
2 * Copyright (c) 2000, 2017 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.widgets;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.events.*;
19 import org.eclipse.swt.graphics.*;
20 import org.eclipse.swt.internal.win32.*;
23 * Instances of this class represent icons that can be placed on the
24 * system tray or task bar status area.
26 * <dt><b>Styles:</b></dt>
28 * <dt><b>Events:</b></dt>
29 * <dd>DefaultSelection, MenuDetect, Selection</dd>
32 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
35 * @see <a href="http://www.eclipse.org/swt/snippets/#tray">Tray, TrayItem snippets</a>
36 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
39 * @noextend This class is not intended to be subclassed by clients.
41 public class TrayItem extends Item {
44 Image image2, highlightImage;
47 boolean visible = true;
50 * Constructs a new instance of this class given its parent
51 * (which must be a <code>Tray</code>) and a style value
52 * describing its behavior and appearance. The item is added
53 * to the end of the items maintained by its parent.
55 * The style value is either one of the style constants defined in
56 * class <code>SWT</code> which is applicable to instances of this
57 * class, or must be built by <em>bitwise OR</em>'ing together
58 * (that is, using the <code>int</code> "|" operator) two or more
59 * of those <code>SWT</code> style constants. The class description
60 * lists the style constants that are applicable to the class.
61 * Style bits are also inherited from superclasses.
64 * @param parent a composite control which will be the parent of the new instance (cannot be null)
65 * @param style the style of control to construct
67 * @exception IllegalArgumentException <ul>
68 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
70 * @exception SWTException <ul>
71 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
72 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
76 * @see Widget#checkSubclass
77 * @see Widget#getStyle
79 public TrayItem (Tray parent, int style) {
80 super (parent, style);
82 parent.createItem (this, parent.getItemCount ());
83 createUpdateWidget (true);
87 * Adds the listener to the collection of listeners who will
88 * be notified when the receiver is selected by the user, by sending
89 * it one of the messages defined in the <code>SelectionListener</code>
92 * <code>widgetSelected</code> is called when the receiver is selected
93 * <code>widgetDefaultSelected</code> is called when the receiver is double-clicked
96 * @param listener the listener which should be notified when the receiver is selected by the user
98 * @exception IllegalArgumentException <ul>
99 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
101 * @exception SWTException <ul>
102 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
103 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
106 * @see SelectionListener
107 * @see #removeSelectionListener
108 * @see SelectionEvent
110 public void addSelectionListener(SelectionListener listener) {
112 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
113 TypedListener typedListener = new TypedListener (listener);
114 addListener (SWT.Selection,typedListener);
115 addListener (SWT.DefaultSelection,typedListener);
119 * Adds the listener to the collection of listeners who will
120 * be notified when the platform-specific context menu trigger
121 * has occurred, by sending it one of the messages defined in
122 * the <code>MenuDetectListener</code> interface.
124 * @param listener the listener which should be notified
126 * @exception IllegalArgumentException <ul>
127 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
129 * @exception SWTException <ul>
130 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
131 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
134 * @see MenuDetectListener
135 * @see #removeMenuDetectListener
139 public void addMenuDetectListener (MenuDetectListener listener) {
141 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
142 TypedListener typedListener = new TypedListener (listener);
143 addListener (SWT.MenuDetect, typedListener);
147 protected void checkSubclass () {
148 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
151 void createUpdateWidget (boolean newIcon) {
152 NOTIFYICONDATA iconData = new NOTIFYICONDATA ();
153 iconData.cbSize = NOTIFYICONDATA.sizeof;
155 * As per MSDN article iconData.uID is unique for every TrayItem
156 * instance(https://msdn.microsoft.com/en-us/library/windows/desktop/
157 * bb762159%28v=vs.85%29.aspx) and this uID value should be specified
158 * during TrayItem instance creation & should be cached and used in
159 * subsequent calls to Shell_NotifyIcon to perform later actions on the
160 * TrayItem instance refer Win10 bug 488739.
162 iconData.uID = id = (newIcon ? display.nextTrayId++ : id);
163 iconData.hWnd = display.hwndMessage;
164 iconData.uFlags = OS.NIF_MESSAGE;
165 iconData.uCallbackMessage = Display.SWT_TRAYICONMSG;
167 * OS.NIM_ADD message should be called only once in a TrayItem instance
168 * life-cycle i.e. in the TrayItem instance creation step only, else
169 * will lead to multiple TrayIcons entries on Win10 refer bug 488739.
171 OS.Shell_NotifyIcon ((newIcon ? OS.NIM_ADD : OS.NIM_MODIFY), iconData);
175 void destroyWidget () {
176 parent.destroyItem (this);
181 * Returns the receiver's highlight image if it has one, or null
184 * @return the receiver's highlight image
186 * @exception SWTException <ul>
187 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
188 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
193 public Image getHighlightImage () {
195 return highlightImage;
199 * Returns the receiver's parent, which must be a <code>Tray</code>.
201 * @return the receiver's parent
203 * @exception SWTException <ul>
204 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
205 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
210 public Tray getParent () {
216 * Returns the receiver's tool tip, or null if it has
219 * @return the receiver's tool tip text
221 * @exception SWTException <ul>
222 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
223 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
228 public ToolTip getToolTip () {
234 * Returns the receiver's tool tip text, or null if it has
237 * @return the receiver's tool tip text
239 * @exception SWTException <ul>
240 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
241 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
244 public String getToolTipText () {
250 * Returns <code>true</code> if the receiver is visible and
251 * <code>false</code> otherwise.
253 * @return the receiver's visibility
255 * @exception SWTException <ul>
256 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
257 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
260 public boolean getVisible () {
265 long messageProc (long hwnd, int msg, long wParam, long lParam) {
267 * Feature in Windows. When the user clicks on the tray
268 * icon, another application may be the foreground window.
269 * This means that the event loop is not running and can
270 * cause problems. For example, if a menu is shown, when
271 * the user clicks outside of the menu to cancel it, the
272 * menu is not hidden until an event is processed. If
273 * another application is the foreground window, then the
274 * menu is not hidden. The fix is to force the tray icon
275 * message window to the foreground when sending an event.
277 switch ((int)lParam) {
278 case OS.WM_LBUTTONDOWN:
279 if (hooks (SWT.Selection)) {
280 OS.SetForegroundWindow (hwnd);
281 sendSelectionEvent (SWT.Selection);
284 case OS.WM_LBUTTONDBLCLK:
285 case OS.WM_RBUTTONDBLCLK:
286 if (hooks (SWT.DefaultSelection)) {
287 OS.SetForegroundWindow (hwnd);
288 sendSelectionEvent (SWT.DefaultSelection);
291 case OS.WM_RBUTTONUP: {
292 if (hooks (SWT.MenuDetect)) {
293 OS.SetForegroundWindow (hwnd);
294 sendEvent (SWT.MenuDetect);
295 // widget could be disposed at this point
296 if (isDisposed()) return 0;
300 case OS.NIN_BALLOONSHOW:
301 if (toolTip != null && !toolTip.visible) {
302 toolTip.visible = true;
303 if (toolTip.hooks (SWT.Show)) {
304 OS.SetForegroundWindow (hwnd);
305 toolTip.sendEvent (SWT.Show);
306 // widget could be disposed at this point
307 if (isDisposed()) return 0;
311 case OS.NIN_BALLOONHIDE:
312 case OS.NIN_BALLOONTIMEOUT:
313 case OS.NIN_BALLOONUSERCLICK:
314 if (toolTip != null) {
315 if (toolTip.visible) {
316 toolTip.visible = false;
317 if (toolTip.hooks (SWT.Hide)) {
318 OS.SetForegroundWindow (hwnd);
319 toolTip.sendEvent (SWT.Hide);
320 // widget could be disposed at this point
321 if (isDisposed()) return 0;
324 if (lParam == OS.NIN_BALLOONUSERCLICK) {
325 if (toolTip.hooks (SWT.Selection)) {
326 OS.SetForegroundWindow (hwnd);
327 toolTip.sendSelectionEvent (SWT.Selection);
328 // widget could be disposed at this point
329 if (isDisposed()) return 0;
335 display.wakeThread ();
340 createUpdateWidget (false);
341 if (!visible) setVisible (false);
342 if (text.length () != 0) setText (text);
343 if (image != null) setImage (image);
344 if (toolTipText != null) setToolTipText (toolTipText);
348 void releaseHandle () {
349 super.releaseHandle ();
354 void releaseWidget () {
355 super.releaseWidget ();
356 if (toolTip != null) toolTip.item = null;
358 if (image2 != null) image2.dispose ();
360 highlightImage = null;
362 NOTIFYICONDATA iconData = new NOTIFYICONDATA ();
363 iconData.cbSize = NOTIFYICONDATA.sizeof;
365 iconData.hWnd = display.hwndMessage;
366 OS.Shell_NotifyIcon (OS.NIM_DELETE, iconData);
370 * Removes the listener from the collection of listeners who will
371 * be notified when the receiver is selected by the user.
373 * @param listener the listener which should no longer be notified
375 * @exception IllegalArgumentException <ul>
376 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
378 * @exception SWTException <ul>
379 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
380 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
383 * @see SelectionListener
384 * @see #addSelectionListener
386 public void removeSelectionListener(SelectionListener listener) {
388 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
389 if (eventTable == null) return;
390 eventTable.unhook (SWT.Selection, listener);
391 eventTable.unhook (SWT.DefaultSelection,listener);
395 * Removes the listener from the collection of listeners who will
396 * be notified when the platform-specific context menu trigger has
399 * @param listener the listener which should no longer be notified
401 * @exception IllegalArgumentException <ul>
402 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
404 * @exception SWTException <ul>
405 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
406 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
409 * @see MenuDetectListener
410 * @see #addMenuDetectListener
414 public void removeMenuDetectListener (MenuDetectListener listener) {
416 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
417 if (eventTable == null) return;
418 eventTable.unhook (SWT.MenuDetect, listener);
422 * Sets the receiver's highlight image.
424 * @param image the new highlight image
426 * @exception IllegalArgumentException <ul>
427 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
429 * @exception SWTException <ul>
430 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
431 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
436 public void setHighlightImage (Image image) {
438 if (image != null && image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
439 highlightImage = image;
443 * Sets the receiver's image.
445 * @param image the new image
447 * @exception IllegalArgumentException <ul>
448 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
450 * @exception SWTException <ul>
451 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
452 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
456 public void setImage (Image image) {
458 if (image != null && image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
459 super.setImage (image);
460 if (image2 != null) image2.dispose ();
467 image2 = Display.createIcon (image);
468 hIcon = image2.handle;
475 NOTIFYICONDATA iconData = new NOTIFYICONDATA ();
476 iconData.cbSize = NOTIFYICONDATA.sizeof;
478 iconData.hWnd = display.hwndMessage;
479 iconData.hIcon = hIcon;
480 iconData.uFlags = OS.NIF_ICON;
481 OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
485 * Sets the receiver's tool tip to the argument, which
486 * may be null indicating that no tool tip should be shown.
488 * @param toolTip the new tool tip (or null)
490 * @exception SWTException <ul>
491 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
492 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
497 public void setToolTip (ToolTip toolTip) {
499 ToolTip oldTip = this.toolTip, newTip = toolTip;
500 if (oldTip != null) oldTip.item = null;
501 this.toolTip = newTip;
502 if (newTip != null) newTip.item = this;
506 * Sets the receiver's tool tip text to the argument, which
507 * may be null indicating that the default tool tip for the
508 * control will be shown. For a control that has a default
509 * tool tip, such as the Tree control on Windows, setting
510 * the tool tip text to an empty string replaces the default,
511 * causing no tool tip text to be shown.
513 * The mnemonic indicator (character '&') is not displayed in a tool tip.
514 * To display a single '&' in the tool tip, the character '&' can be
515 * escaped by doubling it in the string.
518 * NOTE: This operation is a hint and behavior is platform specific, on Windows
519 * for CJK-style mnemonics of the form " (&C)" at the end of the tooltip text
520 * are not shown in tooltip.
523 * @param string the new tool tip text (or null)
525 * @exception SWTException <ul>
526 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
527 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
530 public void setToolTipText (String string) {
532 toolTipText = string;
533 NOTIFYICONDATA iconData = new NOTIFYICONDATA ();
534 if (string != null) {
535 char [] szTip = iconData.szTip;
536 int length = Math.min (szTip.length - 1, string.length ());
537 string.getChars (0, length, szTip, 0);
539 iconData.cbSize = NOTIFYICONDATA.sizeof;
541 iconData.hWnd = display.hwndMessage;
542 iconData.uFlags = OS.NIF_TIP;
543 OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
547 * Makes the receiver visible if the argument is <code>true</code>,
548 * and makes it invisible otherwise.
550 * @param visible the new visibility state
552 * @exception SWTException <ul>
553 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
554 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
557 public void setVisible (boolean visible) {
559 if (this.visible == visible) return;
562 * It is possible (but unlikely), that application
563 * code could have disposed the widget in the show
564 * event. If this happens, just return.
566 sendEvent (SWT.Show);
567 if (isDisposed ()) return;
569 this.visible = visible;
570 NOTIFYICONDATA iconData = new NOTIFYICONDATA ();
571 iconData.cbSize = NOTIFYICONDATA.sizeof;
573 iconData.hWnd = display.hwndMessage;
574 iconData.uFlags = OS.NIF_STATE;
575 iconData.dwState = visible ? 0 : OS.NIS_HIDDEN;
576 iconData.dwStateMask = OS.NIS_HIDDEN;
577 OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
578 if (!visible) sendEvent (SWT.Hide);