1 /******************************************************************************* 2 * Copyright (c) 2000, 2008 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.widgets.ToolTip; 14 15 import java.lang.all; 16 17 import org.eclipse.swt.widgets.Widget; 18 import org.eclipse.swt.widgets.TrayItem; 19 import org.eclipse.swt.widgets.Shell; 20 import org.eclipse.swt.SWT; 21 //import org.eclipse.swt.internal.*; 22 import org.eclipse.swt.internal.gtk.OS; 23 import org.eclipse.swt.graphics.Point; 24 import org.eclipse.swt.graphics.Color; 25 import org.eclipse.swt.events.SelectionListener; 26 import org.eclipse.swt.events.SelectionEvent; 27 import org.eclipse.swt.widgets.TypedListener; 28 import org.eclipse.swt.widgets.Event; 29 import org.eclipse.swt.widgets.Display; 30 31 /** 32 * Instances of this class represent popup windows that are used 33 * to inform or warn the user. 34 * <p> 35 * <dl> 36 * <dt><b>Styles:</b></dt> 37 * <dd>BALLOON, ICON_ERROR, ICON_INFORMATION, ICON_WARNING</dd> 38 * <dt><b>Events:</b></dt> 39 * <dd>Selection</dd> 40 * </dl> 41 * </p><p> 42 * Note: Only one of the styles ICON_ERROR, ICON_INFORMATION, 43 * and ICON_WARNING may be specified. 44 * </p><p> 45 * IMPORTANT: This class is intended to be subclassed <em>only</em> 46 * within the SWT implementation. 47 * </p> 48 * 49 * @see <a href="http://www.eclipse.org/swt/snippets/#tooltips">Tool Tips snippets</a> 50 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 51 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 52 * 53 * @since 3.2 54 */ 55 public class ToolTip : Widget { 56 Shell parent; 57 String text, message; 58 TrayItem item; 59 int x, y; 60 int timerId; 61 void* layoutText, layoutMessage; 62 int [] borderPolygon; 63 bool spikeAbove, autohide; 64 CallbackData timerProcCallbackData; 65 66 static const int BORDER = 5; 67 static const int PADDING = 5; 68 static const int INSET = 4; 69 static const int TIP_HEIGHT = 20; 70 static const int IMAGE_SIZE = 16; 71 static const int DELAY = 8000; 72 73 /** 74 * Constructs a new instance of this class given its parent 75 * and a style value describing its behavior and appearance. 76 * <p> 77 * The style value is either one of the style constants defined in 78 * class <code>SWT</code> which is applicable to instances of this 79 * class, or must be built by <em>bitwise OR</em>'ing together 80 * (that is, using the <code>int</code> "|" operator) two or more 81 * of those <code>SWT</code> style constants. The class description 82 * lists the style constants that are applicable to the class. 83 * Style bits are also inherited from superclasses. 84 * </p> 85 * 86 * @param parent a composite control which will be the parent of the new instance (cannot be null) 87 * @param style the style of control to construct 88 * 89 * @exception IllegalArgumentException <ul> 90 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 91 * </ul> 92 * @exception SWTException <ul> 93 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 94 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 95 * </ul> 96 * 97 * @see SWT#ICON_ERROR 98 * @see SWT#ICON_INFORMATION 99 * @see SWT#ICON_WARNING 100 * @see Widget#checkSubclass 101 * @see Widget#getStyle 102 */ 103 public this (Shell parent, int style) { 104 super (parent, checkStyle (style)); 105 this.parent = parent; 106 createWidget (0); 107 } 108 109 static int checkStyle (int style) { 110 int mask = SWT.ICON_ERROR | SWT.ICON_INFORMATION | SWT.ICON_WARNING; 111 if ((style & mask) is 0) return style; 112 return checkBits (style, SWT.ICON_INFORMATION, SWT.ICON_WARNING, SWT.ICON_ERROR, 0, 0, 0); 113 } 114 115 /** 116 * Adds the listener to the collection of listeners who will 117 * be notified when the receiver is selected by the user, by sending 118 * it one of the messages defined in the <code>SelectionListener</code> 119 * interface. 120 * <p> 121 * <code>widgetSelected</code> is called when the receiver is selected. 122 * <code>widgetDefaultSelected</code> is not called. 123 * </p> 124 * 125 * @param listener the listener which should be notified when the receiver is selected by the user 126 * 127 * @exception IllegalArgumentException <ul> 128 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 129 * </ul> 130 * @exception SWTException <ul> 131 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 132 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 133 * </ul> 134 * 135 * @see SelectionListener 136 * @see #removeSelectionListener 137 * @see SelectionEvent 138 */ 139 public void addSelectionListener (SelectionListener listener) { 140 checkWidget (); 141 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 142 TypedListener typedListener = new TypedListener (listener); 143 addListener (SWT.Selection,typedListener); 144 addListener (SWT.DefaultSelection,typedListener); 145 } 146 147 void configure () { 148 auto screen = OS.gdk_screen_get_default (); 149 OS.gtk_widget_realize (handle); 150 int monitorNumber = OS.gdk_screen_get_monitor_at_window (screen, OS.GTK_WIDGET_WINDOW (handle)); 151 GdkRectangle* dest = new GdkRectangle (); 152 OS.gdk_screen_get_monitor_geometry (screen, monitorNumber, dest); 153 Point point = getSize (dest.width / 4); 154 int w = point.x; 155 int h = point.y; 156 point = getLocation (); 157 int x = point.x; 158 int y = point.y; 159 OS.gtk_window_resize (cast(GtkWindow*)handle, w, h + TIP_HEIGHT); 160 int[] polyline; 161 spikeAbove = dest.height >= y + h + TIP_HEIGHT; 162 if (dest.width >= x + w) { 163 if (dest.height >= y + h + TIP_HEIGHT) { 164 int t = TIP_HEIGHT; 165 polyline = [ 166 0, 5+t, 1, 5+t, 1, 3+t, 3, 1+t, 5, 1+t, 5, t, 167 16, t, 16, 0, 35, t, 168 w-5, t, w-5, 1+t, w-3, 1+t, w-1, 3+t, w-1, 5+t, w, 5+t, 169 w, h-5+t, w-1, h-5+t, w-1, h-3+t, w-2, h-3+t, w-2, h-2+t, w-3, h-2+t, w-3, h-1+t, w-5, h-1+t, w-5, h+t, 170 5, h+t, 5, h-1+t, 3, h-1+t, 3, h-2+t, 2, h-2+t, 2, h-3+t, 1, h-3+t, 1, h-5+t, 0, h-5+t, 171 0, 5+t]; 172 borderPolygon = [ 173 0, 5+t, 1, 4+t, 1, 3+t, 3, 1+t, 4, 1+t, 5, t, 174 16, t, 16, 1, 35, t, 175 w-6, 0+t, w-5, 1+t, w-4, 1+t, w-2, 3+t, w-2, 4+t, w-1, 5+t, 176 w-1, h-6+t, w-2, h-5+t, w-2, h-4+t, w-4, h-2+t, w-5, h-2+t, w-6, h-1+t, 177 5, h-1+t, 4, h-2+t, 3, h-2+t, 1, h-4+t, 1, h-5+t, 0, h-6+t, 178 0, 5+t]; 179 if ((parent.style & SWT.MIRRORED) !is 0) { 180 x -= w - 36; 181 polyline[12] = w-36; 182 polyline[14] = w-16; 183 polyline[16] = w-15; 184 borderPolygon[12] = w-35; 185 borderPolygon[14] = borderPolygon[16] = w-16; 186 } 187 OS.gtk_window_move (cast(GtkWindow*)handle, Math.max(0, x - 17), y); 188 } else { 189 polyline = [ 190 0, 5, 1, 5, 1, 3, 3, 1, 5, 1, 5, 0, 191 w-5, 0, w-5, 1, w-3, 1, w-1, 3, w-1, 5, w, 5, 192 w, h-5, w-1, h-5, w-1, h-3, w-2, h-3, w-2, h-2, w-3, h-2, w-3, h-1, w-5, h-1, w-5, h, 193 35, h, 16, h+TIP_HEIGHT, 16, h, 194 5, h, 5, h-1, 3, h-1, 3, h-2, 2, h-2, 2, h-3, 1, h-3, 1, h-5, 0, h-5, 195 0, 5]; 196 borderPolygon = [ 197 0, 5, 1, 4, 1, 3, 3, 1, 4, 1, 5, 0, 198 w-6, 0, w-5, 1, w-4, 1, w-2, 3, w-2, 4, w-1, 5, 199 w-1, h-6, w-2, h-5, w-2, h-4, w-4, h-2, w-5, h-2, w-6, h-1, 200 35, h-1, 17, h+TIP_HEIGHT-2, 17, h-1, 201 5, h-1, 4, h-2, 3, h-2, 1, h-4, 1, h-5, 0, h-6, 202 0, 5]; 203 if ((parent.style & SWT.MIRRORED) !is 0) { 204 x -= w - 36; 205 polyline [42] = polyline [44] = w-16; 206 polyline [46] = w-35; 207 borderPolygon[36] = borderPolygon[38] = w-17; 208 borderPolygon [40] = w-35; 209 } 210 OS.gtk_window_move (cast(GtkWindow*)handle, Math.max(0, x - 17), y - h - TIP_HEIGHT); 211 } 212 } else { 213 if (dest.height >= y + h + TIP_HEIGHT) { 214 int t = TIP_HEIGHT; 215 polyline = [ 216 0, 5+t, 1, 5+t, 1, 3+t, 3, 1+t, 5, 1+t, 5, t, 217 w-35, t, w-16, 0, w-16, t, 218 w-5, t, w-5, 1+t, w-3, 1+t, w-1, 3+t, w-1, 5+t, w, 5+t, 219 w, h-5+t, w-1, h-5+t, w-1, h-3+t, w-2, h-3+t, w-2, h-2+t, w-3, h-2+t, w-3, h-1+t, w-5, h-1+t, w-5, h+t, 220 5, h+t, 5, h-1+t, 3, h-1+t, 3, h-2+t, 2, h-2+t, 2, h-3+t, 1, h-3+t, 1, h-5+t, 0, h-5+t, 221 0, 5+t]; 222 borderPolygon = [ 223 0, 5+t, 1, 4+t, 1, 3+t, 3, 1+t, 4, 1+t, 5, t, 224 w-35, t, w-17, 2, w-17, t, 225 w-6, t, w-5, 1+t, w-4, 1+t, w-2, 3+t, w-2, 4+t, w-1, 5+t, 226 w-1, h-6+t, w-2, h-5+t, w-2, h-4+t, w-4, h-2+t, w-5, h-2+t, w-6, h-1+t, 227 5, h-1+t, 4, h-2+t, 3, h-2+t, 1, h-4+t, 1, h-5+t, 0, h-6+t, 228 0, 5+t]; 229 if ((parent.style & SWT.MIRRORED) !is 0) { 230 x += w - 35; 231 polyline [12] = polyline [14] = 16; 232 polyline [16] = 35; 233 borderPolygon[12] = borderPolygon[14] = 16; 234 borderPolygon [16] = 35; 235 } 236 OS.gtk_window_move (cast(GtkWindow*)handle, Math.min(dest.width - w, x - w + 17), y); 237 } else { 238 polyline = [ 239 0, 5, 1, 5, 1, 3, 3, 1, 5, 1, 5, 0, 240 w-5, 0, w-5, 1, w-3, 1, w-1, 3, w-1, 5, w, 5, 241 w, h-5, w-1, h-5, w-1, h-3, w-2, h-3, w-2, h-2, w-3, h-2, w-3, h-1, w-5, h-1, w-5, h, 242 w-16, h, w-16, h+TIP_HEIGHT, w-35, h, 243 5, h, 5, h-1, 3, h-1, 3, h-2, 2, h-2, 2, h-3, 1, h-3, 1, h-5, 0, h-5, 244 0, 5]; 245 borderPolygon = [ 246 0, 5, 1, 4, 1, 3, 3, 1, 4, 1, 5, 0, 247 w-6, 0, w-5, 1, w-4, 1, w-2, 3, w-2, 4, w-1, 5, 248 w-1, h-6, w-2, h-5, w-2, h-4, w-4, h-2, w-5, h-2, w-6, h-1, 249 w-17, h-1, w-17, h+TIP_HEIGHT-2, w-36, h-1, 250 5, h-1, 4, h-2, 3, h-2, 1, h-4, 1, h-5, 0, h-6, 251 0, 5]; 252 if ((parent.style & SWT.MIRRORED) !is 0) { 253 x += w - 35; 254 polyline [42] = 35; 255 polyline [44] = polyline [46] = 16; 256 borderPolygon[36] = 35; 257 borderPolygon[38] = borderPolygon [40] = 17; 258 } 259 OS.gtk_window_move (cast(GtkWindow*)handle, Math.min(dest.width - w, x - w + 17), y - h - TIP_HEIGHT); 260 } 261 } 262 auto rgn = OS.gdk_region_polygon ( cast(GdkPoint*)polyline.ptr, cast(int)/*64bit*/polyline.length / 2, OS.GDK_EVEN_ODD_RULE); 263 OS.gtk_widget_realize (handle); 264 auto window = OS.GTK_WIDGET_WINDOW (handle); 265 OS.gdk_window_shape_combine_region (window, rgn, 0, 0); 266 OS.gdk_region_destroy (rgn); 267 } 268 269 override void createHandle (int index) { 270 state |= HANDLE; 271 if ((style & SWT.BALLOON) !is 0) { 272 handle = OS.gtk_window_new (OS.GTK_WINDOW_POPUP); 273 Color background = display.getSystemColor (SWT.COLOR_INFO_BACKGROUND); 274 OS.gtk_widget_modify_bg (handle, OS.GTK_STATE_NORMAL, background.handle); 275 OS.gtk_widget_set_app_paintable (handle, true); 276 } else { 277 handle = cast(GtkWidget*)OS.gtk_tooltips_new (); 278 if (handle is null) SWT.error (SWT.ERROR_NO_HANDLES); 279 /* 280 * Bug in Solaris-GTK. Invoking gtk_tooltips_force_window() 281 * can cause a crash in older versions of GTK. The fix is 282 * to avoid this call if the GTK version is older than 2.2.x. 283 */ 284 if (OS.GTK_VERSION >= OS.buildVERSION (2, 2, 1)) { 285 OS.gtk_tooltips_force_window (cast(GtkTooltips*)handle); 286 } 287 OS.g_object_ref (handle); 288 OS.gtk_object_sink (cast(GtkObject*)handle); 289 } 290 } 291 292 override void createWidget (int index) { 293 super.createWidget (index); 294 text = ""; 295 message = ""; 296 x = y = -1; 297 autohide = true; 298 } 299 300 override void deregister () { 301 super.deregister (); 302 if ((style & SWT.BALLOON) is 0) { 303 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 304 if (tipWindow !is null) display.removeWidget (tipWindow); 305 } 306 } 307 308 override void destroyWidget () { 309 auto topHandle = topHandle (); 310 releaseHandle (); 311 if (topHandle !is null && (state & HANDLE) !is 0) { 312 if ((style & SWT.BALLOON) !is 0) { 313 OS.gtk_widget_destroy (topHandle); 314 } else { 315 OS.g_object_unref (topHandle); 316 } 317 } 318 } 319 320 /** 321 * Returns <code>true</code> if the receiver is automatically 322 * hidden by the platform, and <code>false</code> otherwise. 323 * 324 * @return the receiver's auto hide state 325 * 326 * @exception SWTException <ul> 327 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 328 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 329 * </ul> 330 * 331 */ 332 public bool getAutoHide () { 333 checkWidget (); 334 return autohide; 335 } 336 337 Point getLocation () { 338 int x = this.x; 339 int y = this.y; 340 if (item !is null) { 341 auto itemHandle = item.handle; 342 OS.gtk_widget_realize (itemHandle); 343 auto window = OS.GTK_WIDGET_WINDOW (itemHandle); 344 int px, py; 345 OS.gdk_window_get_origin (cast(GdkWindow*)window, &px, &py); 346 x = px + OS.GTK_WIDGET_WIDTH (itemHandle) / 2; 347 y = py + OS.GTK_WIDGET_HEIGHT (itemHandle) / 2; 348 } 349 if (x is -1 || y is -1) { 350 int px, py; 351 OS.gdk_window_get_pointer (null, &px, &py, null); 352 x = px; 353 y = py; 354 } 355 return new Point(x, y); 356 } 357 358 /** 359 * Returns the receiver's message, which will be an empty 360 * string if it has never been set. 361 * 362 * @return the receiver's message 363 * 364 * @exception SWTException <ul> 365 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 366 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 367 * </ul> 368 */ 369 public String getMessage () { 370 checkWidget (); 371 return message; 372 } 373 374 override String getNameText () { 375 return getText (); 376 } 377 378 /** 379 * Returns the receiver's parent, which must be a <code>Shell</code>. 380 * 381 * @return the receiver's parent 382 * 383 * @exception SWTException <ul> 384 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 385 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 386 * </ul> 387 */ 388 public Shell getParent () { 389 checkWidget (); 390 return parent; 391 } 392 393 Point getSize (int maxWidth) { 394 int textWidth = 0, messageWidth = 0; 395 int w, h; 396 if (layoutText !is null) { 397 OS.pango_layout_set_width (layoutText, -1); 398 OS.pango_layout_get_size (layoutText, &w, &h); 399 textWidth = OS.PANGO_PIXELS (w ); 400 } 401 if (layoutMessage !is null) { 402 OS.pango_layout_set_width (layoutMessage, -1); 403 OS.pango_layout_get_size (layoutMessage, &w, &h); 404 messageWidth = OS.PANGO_PIXELS (w ); 405 } 406 int messageTrim = 2 * INSET + 2 * BORDER + 2 * PADDING; 407 bool hasImage = layoutText !is null && (style & (SWT.ICON_ERROR | SWT.ICON_INFORMATION | SWT.ICON_WARNING)) !is 0; 408 int textTrim = messageTrim + (hasImage ? IMAGE_SIZE : 0); 409 int width = Math.min (maxWidth, Math.max (textWidth + textTrim, messageWidth + messageTrim)); 410 int textHeight = 0, messageHeight = 0; 411 if (layoutText !is null) { 412 OS.pango_layout_set_width (layoutText, (maxWidth - textTrim) * OS.PANGO_SCALE); 413 OS.pango_layout_get_size (layoutText, &w, &h); 414 textHeight = OS.PANGO_PIXELS (h ); 415 } 416 if (layoutMessage !is null) { 417 OS.pango_layout_set_width (layoutMessage, (maxWidth - messageTrim) * OS.PANGO_SCALE); 418 OS.pango_layout_get_size (layoutMessage, &w, &h); 419 messageHeight = OS.PANGO_PIXELS (h); 420 } 421 int height = 2 * BORDER + 2 * PADDING + messageHeight; 422 if (layoutText !is null) height += Math.max (IMAGE_SIZE, textHeight) + 2 * PADDING; 423 return new Point(width, height); 424 } 425 426 /** 427 * Returns the receiver's text, which will be an empty 428 * string if it has never been set. 429 * 430 * @return the receiver's text 431 * 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> 435 * </ul> 436 */ 437 public String getText () { 438 checkWidget (); 439 return text; 440 } 441 442 /** 443 * Returns <code>true</code> if the receiver is visible, and 444 * <code>false</code> otherwise. 445 * <p> 446 * If one of the receiver's ancestors is not visible or some 447 * other condition makes the receiver not visible, this method 448 * may still indicate that it is considered visible even though 449 * it may not actually be showing. 450 * </p> 451 * 452 * @return the receiver's visibility state 453 * 454 * @exception SWTException <ul> 455 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 456 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 457 * </ul> 458 */ 459 public bool getVisible () { 460 checkWidget (); 461 if ((style & SWT.BALLOON) !is 0) return OS.GTK_WIDGET_VISIBLE (handle); 462 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 463 return OS.GTK_WIDGET_VISIBLE (tipWindow); 464 } 465 466 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* event) { 467 notifyListeners (SWT.Selection, new Event ()); 468 setVisible (false); 469 return 0; 470 } 471 472 override int gtk_expose_event (GtkWidget* widget, GdkEventExpose* event) { 473 auto window = OS.GTK_WIDGET_WINDOW (handle); 474 auto gdkGC = cast(GdkGC*)OS.gdk_gc_new (window); 475 OS.gdk_draw_polygon (window, gdkGC, 0, cast(GdkPoint*)borderPolygon.ptr, cast(int)/*64bit*/borderPolygon.length / 2); 476 int x = BORDER + PADDING; 477 int y = BORDER + PADDING; 478 if (spikeAbove) y += TIP_HEIGHT; 479 if (layoutText !is null) { 480 String buffer = null; 481 int id = style & (SWT.ICON_ERROR | SWT.ICON_INFORMATION | SWT.ICON_WARNING); 482 switch (id) { 483 case SWT.ICON_ERROR: buffer = "gtk-dialog-error"; break; 484 case SWT.ICON_INFORMATION: buffer = "gtk-dialog-info"; break; 485 case SWT.ICON_WARNING: buffer = "gtk-dialog-warning"; break; 486 default: 487 } 488 if (buffer !is null) { 489 auto style = OS.gtk_widget_get_default_style (); 490 auto pixbuf = OS.gtk_icon_set_render_icon ( 491 OS.gtk_icon_factory_lookup_default (buffer.ptr), 492 style, 493 OS.GTK_TEXT_DIR_NONE, 494 OS.GTK_STATE_NORMAL, 495 OS.GTK_ICON_SIZE_MENU, 496 null, 497 null); 498 OS.gdk_draw_pixbuf (window, gdkGC, pixbuf, 0, 0, x, y, IMAGE_SIZE, IMAGE_SIZE, OS.GDK_RGB_DITHER_NORMAL, 0, 0); 499 OS.g_object_unref (pixbuf); 500 x += IMAGE_SIZE; 501 } 502 x += INSET; 503 OS.gdk_draw_layout (window, gdkGC, x, y, layoutText); 504 int w, h; 505 OS.pango_layout_get_size (layoutText, &w, &h); 506 y += 2 * PADDING + Math.max (IMAGE_SIZE, OS.PANGO_PIXELS (h )); 507 } 508 if (layoutMessage !is null) { 509 x = BORDER + PADDING + INSET; 510 OS.gdk_draw_layout (window, gdkGC, x, y, layoutMessage); 511 } 512 OS.g_object_unref (gdkGC); 513 return 0; 514 } 515 516 override int gtk_size_allocate (GtkWidget* widget, ptrdiff_t allocation) { 517 Point point = getLocation (); 518 int x = point.x; 519 int y = point.y; 520 auto screen = OS.gdk_screen_get_default (); 521 OS.gtk_widget_realize (widget); 522 int monitorNumber = OS.gdk_screen_get_monitor_at_window (screen, OS.GTK_WIDGET_WINDOW (widget)); 523 GdkRectangle* dest = new GdkRectangle (); 524 OS.gdk_screen_get_monitor_geometry (screen, monitorNumber, dest); 525 int w = OS.GTK_WIDGET_WIDTH (widget); 526 int h = OS.GTK_WIDGET_HEIGHT (widget); 527 if (dest.height < y + h) y -= h; 528 if (dest.width < x + w) x -= w; 529 OS.gtk_window_move (cast(GtkWindow*)widget, x, y); 530 return 0; 531 } 532 533 override void hookEvents () { 534 if ((style & SWT.BALLOON) !is 0) { 535 OS.g_signal_connect_closure (handle, OS.expose_event.ptr, display.closures [EXPOSE_EVENT], false); 536 OS.gtk_widget_add_events (handle, OS.GDK_BUTTON_PRESS_MASK); 537 OS.g_signal_connect_closure (handle, OS.button_press_event.ptr, display.closures [BUTTON_PRESS_EVENT], false); 538 } else { 539 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 540 if (tipWindow !is null) { 541 OS.g_signal_connect_closure (tipWindow, OS.size_allocate.ptr, display.closures [SIZE_ALLOCATE], false); 542 OS.gtk_widget_add_events (tipWindow, OS.GDK_BUTTON_PRESS_MASK); 543 OS.g_signal_connect_closure (tipWindow, OS.button_press_event.ptr, display.closures [BUTTON_PRESS_EVENT], false); 544 } 545 } 546 } 547 548 /** 549 * Returns <code>true</code> if the receiver is visible and all 550 * of the receiver's ancestors are visible and <code>false</code> 551 * otherwise. 552 * 553 * @return the receiver's visibility state 554 * 555 * @exception SWTException <ul> 556 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 557 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 558 * </ul> 559 * 560 * @see #getVisible 561 */ 562 public bool isVisible () { 563 checkWidget (); 564 return getVisible (); 565 } 566 567 override void register () { 568 super.register (); 569 if ((style & SWT.BALLOON) is 0) { 570 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 571 if (tipWindow !is null) display.addWidget (tipWindow, this); 572 } 573 } 574 575 override void releaseWidget () { 576 super.releaseWidget (); 577 if (layoutText !is null) OS.g_object_unref (layoutText); 578 layoutText = null; 579 if (layoutMessage !is null) OS.g_object_unref (layoutMessage); 580 layoutMessage = null; 581 if (timerId !is 0) OS.gtk_timeout_remove(timerId); 582 timerId = 0; 583 text = null; 584 message = null; 585 borderPolygon = null; 586 } 587 588 /** 589 * Removes the listener from the collection of listeners who will 590 * be notified when the receiver is selected by the user. 591 * 592 * @param listener the listener which should no longer be notified 593 * 594 * @exception IllegalArgumentException <ul> 595 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 596 * </ul> 597 * @exception SWTException <ul> 598 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 599 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 600 * </ul> 601 * 602 * @see SelectionListener 603 * @see #addSelectionListener 604 */ 605 public void removeSelectionListener (SelectionListener listener) { 606 checkWidget(); 607 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 608 if (eventTable is null) return; 609 eventTable.unhook (SWT.Selection, listener); 610 eventTable.unhook (SWT.DefaultSelection, listener); 611 } 612 613 /** 614 * Makes the receiver hide automatically when <code>true</code>, 615 * and remain visible when <code>false</code>. 616 * 617 * @param autoHide the auto hide state 618 * 619 * @exception SWTException <ul> 620 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 621 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 622 * </ul> 623 * 624 * @see #getVisible 625 * @see #setVisible 626 */ 627 public void setAutoHide (bool autohide) { 628 checkWidget (); 629 this.autohide = autohide; 630 //TODO - update when visible 631 } 632 633 /** 634 * Sets the location of the receiver, which must be a tooltip, 635 * to the point specified by the arguments which are relative 636 * to the display. 637 * <p> 638 * Note that this is different from most widgets where the 639 * location of the widget is relative to the parent. 640 * </p> 641 * 642 * @param x the new x coordinate for the receiver 643 * @param y the new y coordinate for the receiver 644 * 645 * @exception SWTException <ul> 646 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 647 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 648 * </ul> 649 */ 650 public void setLocation (int x, int y) { 651 checkWidget (); 652 this.x = x; 653 this.y = y; 654 if ((style & SWT.BALLOON) !is 0) { 655 if (OS.GTK_WIDGET_VISIBLE (handle)) configure (); 656 } else { 657 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 658 if (OS.GTK_WIDGET_VISIBLE (tipWindow)) { 659 OS.gtk_window_move (cast(GtkWindow*)tipWindow, x, y); 660 } 661 } 662 } 663 664 /** 665 * Sets the location of the receiver, which must be a tooltip, 666 * to the point specified by the argument which is relative 667 * to the display. 668 * <p> 669 * Note that this is different from most widgets where the 670 * location of the widget is relative to the parent. 671 * </p><p> 672 * Note that the platform window manager ultimately has control 673 * over the location of tooltips. 674 * </p> 675 * 676 * @param location the new location for the receiver 677 * 678 * @exception IllegalArgumentException <ul> 679 * <li>ERROR_NULL_ARGUMENT - if the point is null</li> 680 * </ul> 681 * @exception SWTException <ul> 682 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 683 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 684 * </ul> 685 */ 686 public void setLocation (Point location) { 687 checkWidget (); 688 if (location is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 689 setLocation (location.x, location.y); 690 } 691 692 /** 693 * Sets the receiver's message. 694 * 695 * @param string the new message 696 * 697 * @exception SWTException <ul> 698 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 699 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 700 * </ul> 701 */ 702 public void setMessage (String string) { 703 checkWidget (); 704 // SWT extension: allow null for zero length string 705 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 706 message = string; 707 if ((style & SWT.BALLOON) is 0) return; 708 if (layoutMessage !is null) OS.g_object_unref (layoutMessage); 709 layoutMessage = null; 710 if (message.length !is 0) { 711 layoutMessage = OS.gtk_widget_create_pango_layout (handle, message.toStringzValidPtr()); 712 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 713 OS.pango_layout_set_auto_dir (layoutMessage, false); 714 } 715 OS.pango_layout_set_wrap (layoutMessage, OS.PANGO_WRAP_WORD_CHAR); 716 } 717 if (OS.GTK_WIDGET_VISIBLE (handle)) configure (); 718 } 719 720 /** 721 * Sets the receiver's text. 722 * 723 * @param string the new text 724 * 725 * @exception SWTException <ul> 726 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 727 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 728 * </ul> 729 */ 730 public void setText (String string) { 731 checkWidget (); 732 // SWT extension: allow null for zero length string 733 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 734 text = string; 735 if ((style & SWT.BALLOON) is 0) return; 736 if (layoutText !is null) OS.g_object_unref (layoutText); 737 layoutText = null; 738 if (text.length !is 0) { 739 layoutText = OS.gtk_widget_create_pango_layout (handle, text.toStringzValidPtr()); 740 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 741 OS.pango_layout_set_auto_dir (layoutText, false); 742 } 743 auto boldAttr = OS.pango_attr_weight_new (OS.PANGO_WEIGHT_BOLD); 744 boldAttr.start_index = 0; 745 boldAttr.end_index = cast(int)/*64bit*/text.length+1; 746 auto attrList = OS.pango_attr_list_new (); 747 OS.pango_attr_list_insert (attrList, boldAttr); 748 OS.pango_layout_set_attributes (layoutText, attrList); 749 OS.pango_attr_list_unref (attrList); 750 OS.pango_layout_set_wrap (layoutText, OS.PANGO_WRAP_WORD_CHAR); 751 } 752 if (OS.GTK_WIDGET_VISIBLE (handle)) configure (); 753 } 754 755 /** 756 * Marks the receiver as visible if the argument is <code>true</code>, 757 * and marks it invisible otherwise. 758 * <p> 759 * If one of the receiver's ancestors is not visible or some 760 * other condition makes the receiver not visible, marking 761 * it visible may not actually cause it to be displayed. 762 * </p> 763 * 764 * @param visible the new visibility state 765 * 766 * @exception SWTException <ul> 767 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 768 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 769 * </ul> 770 */ 771 public void setVisible (bool visible) { 772 if (timerId !is 0) OS.gtk_timeout_remove(timerId); 773 timerId = 0; 774 if (visible) { 775 if ((style & SWT.BALLOON) !is 0) { 776 configure (); 777 OS.gtk_widget_show (handle); 778 } else { 779 auto vboxHandle = parent.vboxHandle; 780 String string = text; 781 if (text.length > 0) string ~= "\n\n"; 782 string ~= message; 783 char* buffer = toStringz( string ); 784 OS.gtk_tooltips_set_tip (cast(GtkTooltips*)handle, vboxHandle, buffer, null); 785 auto data = OS.gtk_tooltips_data_get (vboxHandle); 786 OS.GTK_TOOLTIPS_SET_ACTIVE (cast(GtkTooltips*)handle, data); 787 OS.gtk_tooltips_set_tip (cast(GtkTooltips*)handle, vboxHandle, buffer, null); 788 } 789 if (autohide) timerId = display.doWindowTimerAdd( &timerProcCallbackData, DELAY, handle); 790 } else { 791 if ((style & SWT.BALLOON) !is 0) { 792 OS.gtk_widget_hide (handle); 793 } else { 794 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 795 OS.gtk_widget_hide (tipWindow); 796 } 797 } 798 } 799 800 override int timerProc (GtkWidget* widget) { 801 if ((style & SWT.BALLOON) !is 0) { 802 OS.gtk_widget_hide (handle); 803 } else { 804 auto tipWindow = OS.GTK_TOOLTIPS_TIP_WINDOW (cast(GtkTooltips*)handle); 805 OS.gtk_widget_hide (tipWindow); 806 } 807 return 0; 808 } 809 810 }