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.TrayItem; 14 15 import java.lang.all; 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.widgets.Tray; 19 import org.eclipse.swt.widgets.ToolTip; 20 import org.eclipse.swt.widgets.ImageList; 21 import org.eclipse.swt.widgets.Item; 22 import org.eclipse.swt.events.SelectionListener; 23 import org.eclipse.swt.events.SelectionEvent; 24 import org.eclipse.swt.events.MenuDetectListener; 25 import org.eclipse.swt.widgets.TypedListener; 26 import org.eclipse.swt.graphics.Image; 27 import org.eclipse.swt.graphics.Rectangle; 28 import org.eclipse.swt.graphics.Region; 29 import org.eclipse.swt.internal.gtk.OS; 30 31 version(Tango){ 32 import tango.util.Convert; 33 } else { // Phobos 34 import std.conv; 35 } 36 37 /** 38 * Instances of this class represent icons that can be placed on the 39 * system tray or task bar status area. 40 * <p> 41 * <dl> 42 * <dt><b>Styles:</b></dt> 43 * <dd>(none)</dd> 44 * <dt><b>Events:</b></dt> 45 * <dd>DefaultSelection, MenuDetect, Selection</dd> 46 * </dl> 47 * </p><p> 48 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 49 * </p> 50 * 51 * @see <a href="http://www.eclipse.org/swt/snippets/#tray">Tray, TrayItem snippets</a> 52 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 53 * 54 * @since 3.0 55 */ 56 public class TrayItem : Item { 57 Tray parent; 58 ToolTip toolTip; 59 String toolTipText; 60 GtkWidget* imageHandle; 61 GtkWidget* tooltipsHandle; 62 ImageList imageList; 63 64 /** 65 * Constructs a new instance of this class given its parent 66 * (which must be a <code>Tray</code>) and a style value 67 * describing its behavior and appearance. The item is added 68 * to the end of the items maintained by its parent. 69 * <p> 70 * The style value is either one of the style constants defined in 71 * class <code>SWT</code> which is applicable to instances of this 72 * class, or must be built by <em>bitwise OR</em>'ing together 73 * (that is, using the <code>int</code> "|" operator) two or more 74 * of those <code>SWT</code> style constants. The class description 75 * lists the style constants that are applicable to the class. 76 * Style bits are also inherited from superclasses. 77 * </p> 78 * 79 * @param parent a composite control which will be the parent of the new instance (cannot be null) 80 * @param style the style of control to construct 81 * 82 * @exception IllegalArgumentException <ul> 83 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 84 * </ul> 85 * @exception SWTException <ul> 86 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 87 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 88 * </ul> 89 * 90 * @see SWT 91 * @see Widget#checkSubclass 92 * @see Widget#getStyle 93 */ 94 public this (Tray parent, int style) { 95 super (parent, style); 96 this.parent = parent; 97 createWidget (parent.getItemCount ()); 98 } 99 100 /** 101 * Adds the listener to the collection of listeners who will 102 * be notified when the platform-specific context menu trigger 103 * has occurred, by sending it one of the messages defined in 104 * the <code>MenuDetectListener</code> interface. 105 * 106 * @param listener the listener which should be notified 107 * 108 * @exception IllegalArgumentException <ul> 109 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 110 * </ul> 111 * @exception SWTException <ul> 112 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 113 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 114 * </ul> 115 * 116 * @see MenuDetectListener 117 * @see #removeMenuDetectListener 118 * 119 * @since 3.3 120 */ 121 public void addMenuDetectListener (MenuDetectListener listener) { 122 checkWidget (); 123 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 124 TypedListener typedListener = new TypedListener (listener); 125 addListener (SWT.MenuDetect, typedListener); 126 } 127 128 /** 129 * Adds the listener to the collection of listeners who will 130 * be notified when the receiver is selected by the user, by sending 131 * it one of the messages defined in the <code>SelectionListener</code> 132 * interface. 133 * <p> 134 * <code>widgetSelected</code> is called when the receiver is selected 135 * <code>widgetDefaultSelected</code> is called when the receiver is double-clicked 136 * </p> 137 * 138 * @param listener the listener which should be notified when the receiver is selected by the user 139 * 140 * @exception IllegalArgumentException <ul> 141 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 142 * </ul> 143 * @exception SWTException <ul> 144 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 145 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 146 * </ul> 147 * 148 * @see SelectionListener 149 * @see #removeSelectionListener 150 * @see SelectionEvent 151 */ 152 public void addSelectionListener(SelectionListener listener) { 153 checkWidget (); 154 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 155 TypedListener typedListener = new TypedListener (listener); 156 addListener (SWT.Selection, typedListener); 157 addListener (SWT.DefaultSelection, typedListener); 158 } 159 160 protected override void checkSubclass () { 161 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 162 } 163 164 override void createWidget (int index) { 165 super.createWidget (index); 166 parent.createItem (this, index); 167 } 168 169 override void createHandle (int index) { 170 state |= HANDLE; 171 handle = OS.gtk_plug_new (null); 172 if (handle is null) error (SWT.ERROR_NO_HANDLES); 173 imageHandle = OS.gtk_image_new (); 174 if (imageHandle is null) error (SWT.ERROR_NO_HANDLES); 175 OS.gtk_container_add (cast(GtkContainer*)handle, imageHandle); 176 OS.gtk_widget_show (handle); 177 OS.gtk_widget_show (imageHandle); 178 auto id = OS.gtk_plug_get_id (cast(GtkPlug*)handle); 179 int monitor = 0; 180 auto screen = OS.gdk_screen_get_default (); 181 if (screen !is null) { 182 monitor = OS.gdk_screen_get_number (screen); 183 } 184 auto trayAtom = OS.gdk_atom_intern (toStringz("_NET_SYSTEM_TRAY_S" ~ to!(String)(monitor)), true); 185 auto xTrayAtom = OS.gdk_x11_atom_to_xatom (trayAtom); 186 auto xDisplay = OS.GDK_DISPLAY (); 187 auto trayWindow = OS.XGetSelectionOwner (xDisplay, xTrayAtom); 188 auto messageAtom = OS.gdk_atom_intern (toStringz("_NET_SYSTEM_TRAY_OPCODE"), true); 189 auto xMessageAtom = OS.gdk_x11_atom_to_xatom (messageAtom); 190 XClientMessageEvent* event = cast(XClientMessageEvent*)OS.g_malloc (XClientMessageEvent.sizeof); 191 event.type = OS.ClientMessage; 192 event.window = trayWindow; 193 event.message_type = xMessageAtom; 194 event.format = 32; 195 event.data.l [0] = OS.GDK_CURRENT_TIME; 196 event.data.l [1] = OS.SYSTEM_TRAY_REQUEST_DOCK; 197 event.data.l [2] = id; 198 OS.XSendEvent (xDisplay, trayWindow, false, OS.NoEventMask, cast(XEvent*) event); 199 OS.g_free (event); 200 } 201 202 override void deregister () { 203 super.deregister (); 204 display.removeWidget (imageHandle); 205 } 206 207 override void destroyWidget () { 208 parent.destroyItem (this); 209 releaseHandle (); 210 } 211 212 /** 213 * Returns the receiver's parent, which must be a <code>Tray</code>. 214 * 215 * @return the receiver's parent 216 * 217 * @exception SWTException <ul> 218 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 219 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 220 * </ul> 221 * 222 * @since 3.2 223 */ 224 public Tray getParent () { 225 checkWidget (); 226 return parent; 227 } 228 229 /** 230 * Returns the receiver's tool tip, or null if it has 231 * not been set. 232 * 233 * @return the receiver's tool tip text 234 * 235 * @exception SWTException <ul> 236 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 237 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 238 * </ul> 239 * 240 * @since 3.2 241 */ 242 public ToolTip getToolTip () { 243 checkWidget (); 244 return toolTip; 245 } 246 247 /** 248 * Returns the receiver's tool tip text, or null if it has 249 * not been set. 250 * 251 * @return the receiver's tool tip text 252 * 253 * @exception SWTException <ul> 254 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 255 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 256 * </ul> 257 */ 258 public String getToolTipText () { 259 checkWidget (); 260 return toolTipText; 261 } 262 263 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* event) { 264 if (event.type is OS.GDK_3BUTTON_PRESS) return 0; 265 if (event.button is 3 && event.type is OS.GDK_BUTTON_PRESS) { 266 sendEvent (SWT.MenuDetect); 267 return 0; 268 } 269 if (event.type is OS.GDK_2BUTTON_PRESS) { 270 postEvent (SWT.DefaultSelection); 271 } else { 272 postEvent (SWT.Selection); 273 } 274 return 0; 275 } 276 277 override int gtk_size_allocate (GtkWidget* widget, ptrdiff_t allocation) { 278 if (image !is null && image.mask !is null) { 279 if (OS.gdk_drawable_get_depth (image.mask) is 1) { 280 int xoffset = cast(int) Math.floor (OS.GTK_WIDGET_X (widget) + ((OS.GTK_WIDGET_WIDTH (widget) - OS.GTK_WIDGET_REQUISITION_WIDTH (widget)) * 0.5) + 0.5); 281 int yoffset = cast(int) Math.floor (OS.GTK_WIDGET_Y (widget) + ((OS.GTK_WIDGET_HEIGHT (widget) - OS.GTK_WIDGET_REQUISITION_HEIGHT (widget)) * 0.5) + 0.5); 282 Rectangle b = image.getBounds(); 283 auto gdkImage = OS.gdk_drawable_get_image (image.mask, 0, 0, b.width, b.height); 284 if (gdkImage is null) SWT.error(SWT.ERROR_NO_HANDLES); 285 byte[] maskData = (cast(byte*)gdkImage.mem)[ 0 .. gdkImage.bpl * gdkImage.height].dup; 286 Region region = new Region (display); 287 for (int y = 0; y < b.height; y++) { 288 for (int x = 0; x < b.width; x++) { 289 int index = (y * gdkImage.bpl) + (x >> 3); 290 int theByte = maskData [index] & 0xFF; 291 int mask = 1 << (x & 0x7); 292 if ((theByte & mask) !is 0) { 293 region.add (xoffset + x, yoffset + y, 1, 1); 294 } 295 } 296 } 297 OS.g_object_unref (gdkImage); 298 OS.gtk_widget_realize (handle); 299 auto window = OS.GTK_WIDGET_WINDOW (handle); 300 OS.gdk_window_shape_combine_region (window, region.handle, 0, 0); 301 region.dispose (); 302 } 303 } 304 return 0; 305 } 306 307 override void hookEvents () { 308 int eventMask = OS.GDK_BUTTON_PRESS_MASK; 309 OS.gtk_widget_add_events (handle, eventMask); 310 OS.g_signal_connect_closure_by_id (handle, display.signalIds [BUTTON_PRESS_EVENT], 0, display.closures [BUTTON_PRESS_EVENT], false); 311 OS.g_signal_connect_closure_by_id (imageHandle, display.signalIds [SIZE_ALLOCATE], 0, display.closures [SIZE_ALLOCATE], false); 312 } 313 314 /** 315 * Returns <code>true</code> if the receiver is visible and 316 * <code>false</code> otherwise. 317 * 318 * @return the receiver's visibility 319 * 320 * @exception SWTException <ul> 321 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 322 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 323 * </ul> 324 */ 325 public bool getVisible () { 326 checkWidget (); 327 return OS.GTK_WIDGET_VISIBLE (handle); 328 } 329 330 override void register () { 331 super.register (); 332 display.addWidget (imageHandle, this); 333 } 334 335 override void releaseHandle () { 336 if (handle !is null) OS.gtk_widget_destroy (handle); 337 handle = imageHandle = null; 338 super.releaseHandle (); 339 parent = null; 340 } 341 342 override void releaseWidget () { 343 super.releaseWidget (); 344 if (tooltipsHandle !is null) OS.g_object_unref (tooltipsHandle); 345 tooltipsHandle = null; 346 if (imageList !is null) imageList.dispose (); 347 imageList = null; 348 toolTipText = null; 349 } 350 351 /** 352 * Removes the listener from the collection of listeners who will 353 * be notified when the platform-specific context menu trigger has 354 * occurred. 355 * 356 * @param listener the listener which should no longer be notified 357 * 358 * @exception IllegalArgumentException <ul> 359 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 360 * </ul> 361 * @exception SWTException <ul> 362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 364 * </ul> 365 * 366 * @see MenuDetectListener 367 * @see #addMenuDetectListener 368 * 369 * @since 3.3 370 */ 371 public void removeMenuDetectListener (MenuDetectListener listener) { 372 checkWidget (); 373 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 374 if (eventTable is null) return; 375 eventTable.unhook (SWT.MenuDetect, listener); 376 } 377 378 /** 379 * Removes the listener from the collection of listeners who will 380 * be notified when the receiver is selected by the user. 381 * 382 * @param listener the listener which should no longer be notified 383 * 384 * @exception IllegalArgumentException <ul> 385 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 386 * </ul> 387 * @exception SWTException <ul> 388 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 389 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 390 * </ul> 391 * 392 * @see SelectionListener 393 * @see #addSelectionListener 394 */ 395 public void removeSelectionListener (SelectionListener listener) { 396 checkWidget (); 397 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 398 if (eventTable is null) return; 399 eventTable.unhook (SWT.Selection, listener); 400 eventTable.unhook (SWT.DefaultSelection, listener); 401 } 402 403 /** 404 * Sets the receiver's image. 405 * 406 * @param image the new image 407 * 408 * @exception IllegalArgumentException <ul> 409 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li> 410 * </ul> 411 * @exception SWTException <ul> 412 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 413 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 414 * </ul> 415 */ 416 public override void setImage (Image image) { 417 checkWidget (); 418 if (image !is null && image.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); 419 this.image = image; 420 if (image !is null) { 421 Rectangle rect = image.getBounds (); 422 OS.gtk_widget_set_size_request (handle, rect.width, rect.height); 423 if (imageList is null) imageList = new ImageList (); 424 int imageIndex = imageList.indexOf (image); 425 if (imageIndex is -1) { 426 imageIndex = imageList.add (image); 427 } else { 428 imageList.put (imageIndex, image); 429 } 430 auto pixbuf = imageList.getPixbuf (imageIndex); 431 OS.gtk_image_set_from_pixbuf (cast(GtkImage*)imageHandle, pixbuf); 432 OS.gtk_widget_show (imageHandle); 433 } else { 434 OS.gtk_widget_set_size_request (handle, 1, 1); 435 OS.gtk_image_set_from_pixbuf (cast(GtkImage*)imageHandle, null); 436 OS.gtk_widget_hide (imageHandle); 437 } 438 } 439 440 /** 441 * Sets the receiver's tool tip to the argument, which 442 * may be null indicating that no tool tip should be shown. 443 * 444 * @param toolTip the new tool tip (or null) 445 * 446 * @exception SWTException <ul> 447 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 448 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 449 * </ul> 450 * 451 * @since 3.2 452 */ 453 public void setToolTip (ToolTip toolTip) { 454 checkWidget (); 455 ToolTip oldTip = this.toolTip, newTip = toolTip; 456 if (oldTip !is null) oldTip.item = null; 457 this.toolTip = newTip; 458 if (newTip !is null) newTip.item = this; 459 } 460 461 /** 462 * Sets the receiver's tool tip text to the argument, which 463 * may be null indicating that no tool tip text should be shown. 464 * 465 * @param value the new tool tip text (or null) 466 * 467 * @exception SWTException <ul> 468 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 469 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 470 * </ul> 471 */ 472 public void setToolTipText (String string) { 473 checkWidget (); 474 toolTipText = string; 475 char* buffer = null; 476 if (string !is null && string.length > 0) { 477 buffer = toStringz( string ); 478 } 479 if (tooltipsHandle is null) { 480 tooltipsHandle = cast(GtkWidget*)OS.gtk_tooltips_new (); 481 if (tooltipsHandle is null) error (SWT.ERROR_NO_HANDLES); 482 OS.g_object_ref (cast(GObject*)tooltipsHandle); 483 OS.gtk_object_sink (cast(GtkObject*)tooltipsHandle); 484 } 485 OS.gtk_tooltips_set_tip (cast(GtkTooltips*)tooltipsHandle, handle, buffer, null); 486 } 487 488 /** 489 * Makes the receiver visible if the argument is <code>true</code>, 490 * and makes it invisible otherwise. 491 * 492 * @param visible the new visibility state 493 * 494 * @exception SWTException <ul> 495 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 496 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 497 * </ul> 498 */ 499 public void setVisible (bool visible) { 500 checkWidget (); 501 if (OS.GTK_WIDGET_VISIBLE (handle) is visible) return; 502 if (visible) { 503 /* 504 * It is possible (but unlikely), that application 505 * code could have disposed the widget in the show 506 * event. If this happens, just return. 507 */ 508 sendEvent (SWT.Show); 509 if (isDisposed ()) return; 510 OS.gtk_widget_show (handle); 511 } else { 512 OS.gtk_widget_hide (handle); 513 sendEvent (SWT.Hide); 514 } 515 } 516 }