1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 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.*;
21 import org.eclipse.swt.internal.win32.*;
24 * Instances of this class represent popup windows that are used
25 * to inform or warn the user.
27 * <dt><b>Styles:</b></dt>
28 * <dd>BALLOON, ICON_ERROR, ICON_INFORMATION, ICON_WARNING</dd>
29 * <dt><b>Events:</b></dt>
33 * Note: Only one of the styles ICON_ERROR, ICON_INFORMATION,
34 * and ICON_WARNING may be specified.
36 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
39 * @see <a href="http://www.eclipse.org/swt/snippets/#tooltips">Tool Tips snippets</a>
40 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
41 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
44 * @noextend This class is not intended to be subclassed by clients.
46 public class ToolTip extends Widget {
49 String text = "", message = "";
51 boolean autoHide = true, hasLocation, visible;
52 static final int TIMER_ID = 100;
55 * Constructs a new instance of this class given its parent
56 * and a style value describing its behavior and appearance.
58 * The style value is either one of the style constants defined in
59 * class <code>SWT</code> which is applicable to instances of this
60 * class, or must be built by <em>bitwise OR</em>'ing together
61 * (that is, using the <code>int</code> "|" operator) two or more
62 * of those <code>SWT</code> style constants. The class description
63 * lists the style constants that are applicable to the class.
64 * Style bits are also inherited from superclasses.
67 * @param parent a composite control which will be the parent of the new instance (cannot be null)
68 * @param style the style of control to construct
70 * @exception IllegalArgumentException <ul>
71 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
73 * @exception SWTException <ul>
74 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
75 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
80 * @see SWT#ICON_INFORMATION
81 * @see SWT#ICON_WARNING
82 * @see Widget#checkSubclass
83 * @see Widget#getStyle
85 public ToolTip (Shell parent, int style) {
86 super (parent, checkStyle (style));
88 checkOrientation (parent);
89 parent.createToolTip (this);
92 static int checkStyle (int style) {
93 int mask = SWT.ICON_ERROR | SWT.ICON_INFORMATION | SWT.ICON_WARNING;
94 if ((style & mask) == 0) return style;
95 return checkBits (style, SWT.ICON_INFORMATION, SWT.ICON_WARNING, SWT.ICON_ERROR, 0, 0, 0);
99 * Adds the listener to the collection of listeners who will
100 * be notified when the receiver is selected by the user, by sending
101 * it one of the messages defined in the <code>SelectionListener</code>
104 * <code>widgetSelected</code> is called when the receiver is selected.
105 * <code>widgetDefaultSelected</code> is not called.
108 * @param listener the listener which should be notified when the receiver is selected by the user
110 * @exception IllegalArgumentException <ul>
111 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
113 * @exception SWTException <ul>
114 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
115 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
118 * @see SelectionListener
119 * @see #removeSelectionListener
120 * @see SelectionEvent
122 public void addSelectionListener (SelectionListener listener) {
124 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
125 TypedListener typedListener = new TypedListener(listener);
126 addListener (SWT.Selection,typedListener);
127 addListener (SWT.DefaultSelection,typedListener);
131 void destroyWidget () {
132 if (parent != null) parent.destroyToolTip (this);
137 * Returns <code>true</code> if the receiver is automatically
138 * hidden by the platform, and <code>false</code> otherwise.
140 * @return the receiver's auto hide state
142 * @exception SWTException <ul>
143 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
144 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
148 public boolean getAutoHide () {
154 * Returns the receiver's message, which will be an empty
155 * string if it has never been set.
157 * @return the receiver's message
159 * @exception SWTException <ul>
160 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
161 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
164 public String getMessage () {
170 * Returns the receiver's parent, which must be a <code>Shell</code>.
172 * @return the receiver's parent
174 * @exception SWTException <ul>
175 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
176 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
179 public Shell getParent () {
185 * Returns the receiver's text, which will be an empty
186 * string if it has never been set.
188 * @return the receiver's text
190 * @exception SWTException <ul>
191 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
192 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
195 public String getText () {
201 * Returns <code>true</code> if the receiver is visible, and
202 * <code>false</code> otherwise.
204 * If one of the receiver's ancestors is not visible or some
205 * other condition makes the receiver not visible, this method
206 * may still indicate that it is considered visible even though
207 * it may not actually be showing.
210 * @return the receiver's visibility state
212 * @exception SWTException <ul>
213 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
214 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
217 public boolean getVisible () {
219 if (item != null) return visible;
220 long hwndToolTip = hwndToolTip ();
221 if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) {
222 TOOLINFO lpti = new TOOLINFO ();
223 lpti.cbSize = TOOLINFO.sizeof;
224 if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
225 return (lpti.uFlags & OS.TTF_IDISHWND) == 0 && lpti.uId == id;
232 long hwnd = parent.handle;
233 long hmonitor = OS.MonitorFromWindow (hwnd, OS.MONITOR_DEFAULTTONEAREST);
234 MONITORINFO lpmi = new MONITORINFO ();
235 lpmi.cbSize = MONITORINFO.sizeof;
236 OS.GetMonitorInfo (hmonitor, lpmi);
237 int maxWidth = lpmi.rcWork_right - lpmi.rcWork_left;
241 long hwndToolTip () {
242 return (style & SWT.BALLOON) != 0 ? parent.balloonTipHandle () : parent.toolTipHandle ();
246 * Returns <code>true</code> if the receiver is visible and all
247 * of the receiver's ancestors are visible and <code>false</code>
250 * @return the receiver's visibility state
252 * @exception SWTException <ul>
253 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
254 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
259 public boolean isVisible () {
261 if (item != null) return getVisible () && item.getVisible ();
262 return getVisible ();
266 void releaseHandle () {
267 super.releaseHandle ();
274 void releaseWidget () {
275 super.releaseWidget ();
278 long hwndToolTip = hwndToolTip ();
279 if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) {
280 TOOLINFO lpti = new TOOLINFO ();
281 lpti.cbSize = TOOLINFO.sizeof;
282 if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
283 if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) {
284 if (lpti.uId == id) {
285 OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 0, lpti);
286 OS.SendMessage (hwndToolTip, OS.TTM_POP, 0, 0);
287 OS.KillTimer (hwndToolTip, TIMER_ID);
294 if (item != null && item.toolTip == this) {
298 text = message = null;
302 * Removes the listener from the collection of listeners who will
303 * be notified when the receiver is selected by the user.
305 * @param listener the listener which should no longer be notified
307 * @exception IllegalArgumentException <ul>
308 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
310 * @exception SWTException <ul>
311 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
312 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
315 * @see SelectionListener
316 * @see #addSelectionListener
318 public void removeSelectionListener (SelectionListener listener) {
320 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
321 if (eventTable == null) return;
322 eventTable.unhook (SWT.Selection, listener);
323 eventTable.unhook (SWT.DefaultSelection,listener);
327 * Makes the receiver hide automatically when <code>true</code>,
328 * and remain visible when <code>false</code>.
330 * @param autoHide the auto hide state
332 * @exception SWTException <ul>
333 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
334 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
340 public void setAutoHide (boolean autoHide) {
342 this.autoHide = autoHide;
343 //TODO - update when visible
347 * Sets the location of the receiver, which must be a tooltip,
348 * to the point specified by the arguments which are relative
351 * Note that this is different from most widgets where the
352 * location of the widget is relative to the parent.
355 * @param x the new x coordinate for the receiver
356 * @param y the new y coordinate for the receiver
358 * @exception SWTException <ul>
359 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
360 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
363 public void setLocation (int x, int y) {
365 setLocationInPixels(DPIUtil.autoScaleUp(x), DPIUtil.autoScaleUp(y));
368 void setLocationInPixels (int x, int y) {
372 //TODO - update when visible
376 * Sets the location of the receiver, which must be a tooltip,
377 * to the point specified by the argument which is relative
380 * Note that this is different from most widgets where the
381 * location of the widget is relative to the parent.
383 * Note that the platform window manager ultimately has control
384 * over the location of tooltips.
387 * @param location the new location for the receiver
389 * @exception IllegalArgumentException <ul>
390 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
392 * @exception SWTException <ul>
393 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
394 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
397 public void setLocation (Point location) {
399 if (location == null) error (SWT.ERROR_NULL_ARGUMENT);
400 location = DPIUtil.autoScaleUp(location);
401 setLocationInPixels(location.x, location.y);
405 * Sets the receiver's message.
407 * @param string the new message
409 * @exception IllegalArgumentException <ul>
410 * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
412 * @exception SWTException <ul>
413 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
414 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
417 public void setMessage (String string) {
419 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
425 * Sets the receiver's text.
427 * @param string the new text
429 * @exception IllegalArgumentException <ul>
430 * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
432 * @exception SWTException <ul>
433 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
434 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
437 public void setText (String string) {
439 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
441 //TODO - update when visible
442 //TODO - support text direction (?)
445 void updateMessage () {
446 long hwnd = hwndToolTip();
447 if (OS.SendMessage (hwnd, OS.TTM_GETCURRENTTOOL, 0, 0) != 0) {
448 TOOLINFO lpti = new TOOLINFO ();
449 lpti.cbSize = TOOLINFO.sizeof;
450 if (OS.SendMessage (hwnd, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
451 if (message != null && message.length() > 0) {
452 long hHeap = OS.GetProcessHeap ();
453 TCHAR buffer = new TCHAR (0, message, true);
454 int byteCount = buffer.length () * TCHAR.sizeof;
455 lpti.lpszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
456 OS.MoveMemory (lpti.lpszText, buffer, byteCount);
457 OS.SendMessage (hwnd, OS.TTM_UPDATETIPTEXT, 0, lpti);
458 OS.HeapFree(hHeap, 0, lpti.lpszText);
462 * Bug 498895: When empty message string is set, then
463 * underlying native tool-tip object goes into dirty state &
464 * becomes unusable. Hence reset TOOLINFO#lpszText message
465 * text field to -1 (which is the default initial value of
466 * this field when fetch using TTM_GETCURRENTTOOL API call)
467 * to set empty message string at native level successfully.
470 OS.SendMessage (hwnd, OS.TTM_UPDATETIPTEXT, 0, lpti);
477 * Marks the receiver as visible if the argument is <code>true</code>,
478 * and marks it invisible otherwise.
480 * If one of the receiver's ancestors is not visible or some
481 * other condition makes the receiver not visible, marking
482 * it visible may not actually cause it to be displayed.
485 * @param visible the new visibility state
487 * @exception SWTException <ul>
488 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
489 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
492 public void setVisible (boolean visible) {
494 if (visible == getVisible ()) return;
496 long hwnd = parent.handle;
497 TOOLINFO lpti = new TOOLINFO ();
498 lpti.cbSize = TOOLINFO.sizeof;
501 long hwndToolTip = hwndToolTip ();
502 Shell shell = parent.getShell ();
503 if (text.length () != 0) {
504 int icon = OS.TTI_NONE;
505 if ((style & SWT.ICON_INFORMATION) != 0) icon = OS.TTI_INFO;
506 if ((style & SWT.ICON_WARNING) != 0) icon = OS.TTI_WARNING;
507 if ((style & SWT.ICON_ERROR) != 0) icon = OS.TTI_ERROR;
508 shell.setToolTipTitle (hwndToolTip, text, icon);
510 shell.setToolTipTitle (hwndToolTip, null, 0);
512 OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, getWidth ());
516 POINT pt = new POINT ();
517 if (OS.GetCursorPos (pt)) {
522 long lParam = OS.MAKELPARAM (nX, nY);
523 OS.SendMessage (hwndToolTip, OS.TTM_TRACKPOSITION, 0, lParam);
526 * Feature in Windows. Windows will not show a tool tip
527 * if the cursor is outside the parent window (even on XP,
528 * TTM_POPUP will not do this). The fix is to temporarily
529 * move the cursor into the tool window, show the tool tip,
530 * and then restore the cursor.
532 POINT pt = new POINT ();
533 OS.GetCursorPos (pt);
534 RECT rect = new RECT ();
535 OS.GetClientRect (hwnd, rect);
536 OS.MapWindowPoints (hwnd, 0, rect, 2);
537 if (!OS.PtInRect (rect, pt)) {
538 long hCursor = OS.GetCursor ();
540 OS.SetCursorPos (rect.left, rect.top);
541 OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 1, lpti);
542 OS.SetCursorPos (pt.x, pt.y);
543 OS.SetCursor (hCursor);
545 OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 1, lpti);
548 int time = (int)OS.SendMessage (hwndToolTip, OS.TTM_GETDELAYTIME, OS.TTDT_AUTOPOP, 0);
549 OS.SetTimer (hwndToolTip, TIMER_ID, time, 0);
552 OS.SendMessage (hwndToolTip, OS.TTM_TRACKACTIVATE, 0, lpti);
553 OS.SendMessage (hwndToolTip, OS.TTM_POP, 0, 0);
554 OS.KillTimer (hwndToolTip, TIMER_ID);
560 NOTIFYICONDATA iconData = new NOTIFYICONDATA ();
561 char [] szInfoTitle = iconData.szInfoTitle;
562 int length1 = Math.min (szInfoTitle.length - 1, text.length ());
563 text.getChars (0, length1, szInfoTitle, 0);
564 char [] szInfo = iconData.szInfo;
565 int length2 = Math.min (szInfo.length - 1, message.length ());
566 message.getChars (0, length2, szInfo, 0);
567 Display display = item.getDisplay ();
568 iconData.cbSize = NOTIFYICONDATA.sizeof;
569 iconData.uID = item.id;
570 iconData.hWnd = display.hwndMessage;
571 iconData.uFlags = OS.NIF_INFO;
572 if ((style & SWT.ICON_INFORMATION) != 0) iconData.dwInfoFlags = OS.NIIF_INFO;
573 if ((style & SWT.ICON_WARNING) != 0) iconData.dwInfoFlags = OS.NIIF_WARNING;
574 if ((style & SWT.ICON_ERROR) != 0) iconData.dwInfoFlags = OS.NIIF_ERROR;
575 sendEvent (SWT.Show);
576 this.visible = OS.Shell_NotifyIcon (OS.NIM_MODIFY, iconData);
578 //TODO - hide the tray item