]> Pileus Git - ~andy/gtk/blob - gtk/gtkstatusicon.c
allow positioning bubbles
[~andy/gtk] / gtk / gtkstatusicon.c
1 /* gtkstatusicon.c:
2  *
3  * Copyright (C) 2003 Sun Microsystems, Inc.
4  * Copyright (C) 2005 Hans Breuer <hans@breuer.org>
5  * Copyright (C) 2005 Novell, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors:
23  *      Mark McLoughlin <mark@skynet.ie>
24  *      Hans Breuer <hans@breuer.org>
25  *      Tor Lillqvist <tml@novell.com>
26  */
27
28 #include <config.h>
29 #include <string.h>
30
31 #include "gtkstatusicon.h"
32
33 #include "gtkintl.h"
34 #include "gtkiconfactory.h"
35 #include "gtkmarshalers.h"
36 #include "gtktooltips.h"
37 #include "gtktrayicon.h"
38
39 #include "gtkprivate.h"
40 #include "gtkwidget.h"
41
42 #include "gtkalias.h"
43
44 #ifdef GDK_WINDOWING_WIN32
45 #include "gtkicontheme.h"
46 #include "gtklabel.h"
47
48 #include "win32/gdkwin32.h"
49 #define WM_GTK_TRAY_NOTIFICATION (WM_USER+1)
50 #endif
51
52 #define BLINK_TIMEOUT 500
53
54 enum
55 {
56   PROP_0,
57   PROP_PIXBUF,
58   PROP_FILE,
59   PROP_STOCK,
60   PROP_ICON_NAME,
61   PROP_STORAGE_TYPE,
62   PROP_SIZE,
63   PROP_VISIBLE,
64   PROP_BLINKING
65 };
66
67 enum 
68 {
69   ACTIVATE_SIGNAL,
70   POPUP_MENU_SIGNAL,
71   SIZE_CHANGED_SIGNAL,
72   LAST_SIGNAL
73 };
74
75 struct _GtkStatusIconPrivate
76 {
77 #ifdef GDK_WINDOWING_X11
78   GtkWidget    *tray_icon;
79   GtkWidget    *image;
80   GtkTooltips  *tooltips;
81 #endif
82
83 #ifdef GDK_WINDOWING_WIN32
84   GtkWidget     *dummy_widget;
85   NOTIFYICONDATAW nid;
86 #endif
87
88   gint          size;
89
90   gint          image_width;
91   gint          image_height;
92
93   GtkImageType  storage_type;
94
95   union
96     {
97       GdkPixbuf *pixbuf;
98       gchar     *stock_id;
99       gchar     *icon_name;
100     } image_data;
101
102   GdkPixbuf    *blank_icon;
103   guint         blinking_timeout;
104
105   guint         blinking : 1;
106   guint         blink_off : 1;
107   guint         visible : 1;
108 };
109
110 static void     gtk_status_icon_finalize         (GObject        *object);
111 static void     gtk_status_icon_set_property     (GObject        *object,
112                                                   guint           prop_id,
113                                                   const GValue   *value,
114                                                   GParamSpec     *pspec);
115 static void     gtk_status_icon_get_property     (GObject        *object,
116                                                   guint           prop_id,
117                                                   GValue         *value,
118                                                   GParamSpec     *pspec);
119
120 #ifdef GDK_WINDOWING_X11
121 static void     gtk_status_icon_size_allocate    (GtkStatusIcon  *status_icon,
122                                                   GtkAllocation  *allocation);
123 #endif
124 static gboolean gtk_status_icon_button_press     (GtkStatusIcon  *status_icon,
125                                                   GdkEventButton *event);
126 static void     gtk_status_icon_disable_blinking (GtkStatusIcon  *status_icon);
127 static void     gtk_status_icon_reset_image_data (GtkStatusIcon  *status_icon);
128                                            
129
130 static guint status_icon_signals [LAST_SIGNAL] = { 0 };
131
132 G_DEFINE_TYPE (GtkStatusIcon, gtk_status_icon, G_TYPE_OBJECT)
133
134 static void
135 gtk_status_icon_class_init (GtkStatusIconClass *class)
136 {
137   GObjectClass *gobject_class = (GObjectClass *) class;
138
139   gobject_class->finalize     = gtk_status_icon_finalize;
140   gobject_class->set_property = gtk_status_icon_set_property;
141   gobject_class->get_property = gtk_status_icon_get_property;
142
143   g_object_class_install_property (gobject_class,
144                                    PROP_PIXBUF,
145                                    g_param_spec_object ("pixbuf",
146                                                         P_("Pixbuf"),
147                                                         P_("A GdkPixbuf to display"),
148                                                         GDK_TYPE_PIXBUF,
149                                                         GTK_PARAM_READWRITE));
150
151   g_object_class_install_property (gobject_class,
152                                    PROP_FILE,
153                                    g_param_spec_string ("file",
154                                                         P_("Filename"),
155                                                         P_("Filename to load and display"),
156                                                         NULL,
157                                                         GTK_PARAM_WRITABLE));
158
159   g_object_class_install_property (gobject_class,
160                                    PROP_STOCK,
161                                    g_param_spec_string ("stock",
162                                                         P_("Stock ID"),
163                                                         P_("Stock ID for a stock image to display"),
164                                                         NULL,
165                                                         GTK_PARAM_READWRITE));
166   
167   g_object_class_install_property (gobject_class,
168                                    PROP_ICON_NAME,
169                                    g_param_spec_string ("icon-name",
170                                                         P_("Icon Name"),
171                                                         P_("The name of the icon from the icon theme"),
172                                                         NULL,
173                                                         GTK_PARAM_READWRITE));
174   
175   g_object_class_install_property (gobject_class,
176                                    PROP_STORAGE_TYPE,
177                                    g_param_spec_enum ("storage-type",
178                                                       P_("Storage type"),
179                                                       P_("The representation being used for image data"),
180                                                       GTK_TYPE_IMAGE_TYPE,
181                                                       GTK_IMAGE_EMPTY,
182                                                       GTK_PARAM_READABLE));
183
184   g_object_class_install_property (gobject_class,
185                                    PROP_SIZE,
186                                    g_param_spec_int ("size",
187                                                      P_("Size"),
188                                                      P_("The size of the icon"),
189                                                      0,
190                                                      G_MAXINT,
191                                                      0,
192                                                      GTK_PARAM_READABLE));
193
194   g_object_class_install_property (gobject_class,
195                                    PROP_BLINKING,
196                                    g_param_spec_boolean ("blinking",
197                                                          P_("Blinking"),
198                                                          P_("Whether or not the status icon is blinking"),
199                                                          FALSE,
200                                                          GTK_PARAM_READWRITE));
201
202   g_object_class_install_property (gobject_class,
203                                    PROP_VISIBLE,
204                                    g_param_spec_boolean ("visible",
205                                                          P_("Visible"),
206                                                          P_("Whether or not the status icon is visible"),
207                                                          TRUE,
208                                                          GTK_PARAM_READWRITE));
209
210
211   /**
212    * GtkStatusIcon::activate:
213    * @status_icon: the object which received the signal
214    *
215    * Gets emitted when the user activates the status icon. 
216    * If and how status icons can activated is platform-dependent.
217    *
218    * Since: 2.10
219    */
220   status_icon_signals [ACTIVATE_SIGNAL] =
221     g_signal_new (I_("activate"),
222                   G_TYPE_FROM_CLASS (gobject_class),
223                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
224                   G_STRUCT_OFFSET (GtkStatusIconClass, activate),
225                   NULL,
226                   NULL,
227                   g_cclosure_marshal_VOID__VOID,
228                   G_TYPE_NONE,
229                   0);
230
231   /**
232    * GtkStatusIcon::popup-menu:
233    * @status_icon: the object which received the signal
234    * @button: the button that was pressed, or 0 if the 
235    *   signal is not emitted in response to a button press event
236    * @activate_time: the timestamp of the event that
237    *   triggered the signal emission
238    *
239    * Gets emitted when the user brings up the context menu
240    * of the status icon. Whether status icons can have context 
241    * menus and how these are activated is platform-dependent.
242    *
243    * The @button and @activate_timeout parameters should be 
244    * passed as the last to arguments to gtk_menu_popup().
245    *
246    * Since: 2.10
247    */
248   status_icon_signals [POPUP_MENU_SIGNAL] =
249     g_signal_new (I_("popup-menu"),
250                   G_TYPE_FROM_CLASS (gobject_class),
251                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
252                   G_STRUCT_OFFSET (GtkStatusIconClass, popup_menu),
253                   NULL,
254                   NULL,
255                   _gtk_marshal_VOID__UINT_UINT,
256                   G_TYPE_NONE,
257                   2,
258                   G_TYPE_UINT,
259                   G_TYPE_UINT);
260
261   /**
262    * GtkStatusIcon::size-changed:
263    * @status_icon: the object which received the signal
264    * @size: the new size
265    *
266    * Gets emitted when the size available for the image
267    * changes, e.g. because the notification area got resized.
268    *
269    * Return value: %TRUE if the icon was updated for the new
270    * size. Otherwise, GTK+ will scale the icon as necessary.
271    *
272    * Since: 2.10
273    */
274   status_icon_signals [SIZE_CHANGED_SIGNAL] =
275     g_signal_new (I_("size-changed"),
276                   G_TYPE_FROM_CLASS (gobject_class),
277                   G_SIGNAL_RUN_LAST,
278                   G_STRUCT_OFFSET (GtkStatusIconClass, size_changed),
279                   g_signal_accumulator_true_handled,
280                   NULL,
281                   _gtk_marshal_BOOLEAN__INT,
282                   G_TYPE_BOOLEAN,
283                   1,
284                   G_TYPE_INT);
285
286   g_type_class_add_private (class, sizeof (GtkStatusIconPrivate));
287 }
288
289 #ifdef GDK_WINDOWING_WIN32
290
291 static void
292 build_button_event (GdkEventButton *e,
293                     GdkEventType    type,
294                     guint           button)
295 {
296   POINT pos;
297   GdkRectangle monitor0;
298
299   /* We know that gdk/win32 puts the primary monitor at index 0 */
300   gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor0);
301   e->type = type;
302   e->window = gdk_get_default_root_window ();
303   e->send_event = TRUE;
304   e->time = GetTickCount ();
305   GetCursorPos (&pos);
306   e->x = pos.x + monitor0.x;
307   e->y = pos.y + monitor0.y;
308   e->axes = NULL;
309   e->state = 0;
310   e->button = button;
311   e->device = gdk_display_get_default ()->core_pointer;
312   e->x_root = e->x;
313   e->y_root = e->y;
314 }
315
316 static LRESULT CALLBACK
317 wndproc (HWND   hwnd,
318          UINT   message,
319          WPARAM wparam,
320          LPARAM lparam)
321 {
322   if (message == WM_GTK_TRAY_NOTIFICATION)
323     {
324       GdkEventButton e;
325       GtkStatusIcon *status_icon = GTK_STATUS_ICON (wparam);
326       
327       switch (lparam)
328         {
329         case WM_LBUTTONDOWN:
330         case WM_RBUTTONDOWN:
331           build_button_event (&e, GDK_BUTTON_PRESS,
332                               (lparam == WM_LBUTTONDOWN) ? 1 : 3);
333           gtk_status_icon_button_press (status_icon, &e);
334           break;
335         default :
336           break;
337         }
338         return 0;
339     }
340   else
341     {
342       return DefWindowProc (hwnd, message, wparam, lparam);
343     }
344 }
345
346 static HWND
347 create_tray_observer (void)
348 {
349   WNDCLASS    wclass;
350   static HWND hwnd = NULL;
351   ATOM        klass;
352   HINSTANCE   hmodule = GetModuleHandle (NULL);
353
354   if (hwnd)
355     return hwnd;
356
357   memset (&wclass, 0, sizeof(WNDCLASS));
358   wclass.lpszClassName = "gtkstatusicon-observer";
359   wclass.lpfnWndProc   = wndproc;
360   wclass.hInstance     = hmodule;
361
362   klass = RegisterClass (&wclass);
363   if (!klass)
364     return NULL;
365
366   hwnd = CreateWindow (MAKEINTRESOURCE(klass),
367                        NULL, WS_POPUP,
368                        0, 0, 1, 1, NULL, NULL,
369                        hmodule, NULL);
370   if (!hwnd)
371     {
372       UnregisterClass (MAKEINTRESOURCE(klass), hmodule);
373       return NULL;
374     }
375
376   return hwnd;
377 }
378
379 #endif
380
381 static void
382 gtk_status_icon_init (GtkStatusIcon *status_icon)
383 {
384   GtkStatusIconPrivate *priv;
385
386   priv = G_TYPE_INSTANCE_GET_PRIVATE (status_icon, GTK_TYPE_STATUS_ICON,
387                                       GtkStatusIconPrivate);
388   status_icon->priv = priv;
389   
390   priv->storage_type = GTK_IMAGE_EMPTY;
391   priv->visible      = TRUE;
392
393 #ifdef GDK_WINDOWING_X11
394   priv->size         = 0;
395   priv->image_width  = 0;
396   priv->image_height = 0;
397
398   priv->tray_icon = GTK_WIDGET (_gtk_tray_icon_new (NULL));
399
400   gtk_widget_add_events (GTK_WIDGET (priv->tray_icon),
401                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
402
403   g_signal_connect_swapped (priv->tray_icon, "button-press-event",
404                             G_CALLBACK (gtk_status_icon_button_press), status_icon);
405   priv->image = gtk_image_new ();
406   gtk_container_add (GTK_CONTAINER (priv->tray_icon), priv->image);
407
408   g_signal_connect_swapped (priv->image, "size-allocate",
409                             G_CALLBACK (gtk_status_icon_size_allocate), status_icon);
410
411   gtk_widget_show (priv->image);
412   gtk_widget_show (priv->tray_icon);
413
414   status_icon->priv->tooltips = gtk_tooltips_new ();
415   g_object_ref_sink (priv->tooltips);
416 #endif
417
418 #ifdef GDK_WINDOWING_WIN32
419
420   /* Code to get position and orientation of Windows taskbar. Not needed
421    * currently, kept for reference.
422    */
423 #if 0
424   {
425     APPBARDATA abd;
426     
427     abd.cbSize = sizeof (abd);
428     SHAppBarMessage (ABM_GETTASKBARPOS, &abd);
429     if (abd.rc.bottom - abd.rc.top > abd.rc.right - abd.rc.left)
430       orientation = GTK_ORIENTATION_VERTICAL;
431     else
432       orientation = GTK_ORIENTATION_HORIZONTAL;
433   }
434 #endif
435
436   /* Are the system tray icons always 16 pixels square? */
437   priv->size         = 16;
438   priv->image_width  = 16;
439   priv->image_height = 16;
440
441   priv->dummy_widget = gtk_label_new ("");
442
443   memset (&priv->nid, 0, sizeof (priv->nid));
444
445   priv->nid.hWnd = create_tray_observer ();
446   priv->nid.uID = GPOINTER_TO_UINT (status_icon);
447   priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
448   priv->nid.uFlags = NIF_MESSAGE;
449
450   if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
451     {
452       g_warning ("%s:%d:Shell_NotifyIcon(NIM_ADD) failed", __FILE__, __LINE__-2);
453       priv->nid.hWnd = NULL;
454     }
455 #endif
456 }
457
458 static void
459 gtk_status_icon_finalize (GObject *object)
460 {
461   GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
462   GtkStatusIconPrivate *priv = status_icon->priv;
463
464   gtk_status_icon_disable_blinking (status_icon);
465   
466   gtk_status_icon_reset_image_data (status_icon);
467
468   if (priv->blank_icon)
469     g_object_unref (priv->blank_icon);
470   priv->blank_icon = NULL;
471
472 #ifdef GDK_WINDOWING_X11
473   if (priv->tooltips)
474     g_object_unref (priv->tooltips);
475   priv->tooltips = NULL;
476
477   gtk_widget_destroy (priv->tray_icon);
478 #endif
479
480 #ifdef GDK_WINDOWING_WIN32
481   if (priv->nid.hWnd != NULL && priv->visible)
482     Shell_NotifyIconW (NIM_DELETE, &priv->nid);
483
484   gtk_widget_destroy (priv->dummy_widget);
485 #endif
486
487   G_OBJECT_CLASS (gtk_status_icon_parent_class)->finalize (object);
488 }
489
490 static void
491 gtk_status_icon_set_property (GObject      *object,
492                               guint         prop_id,
493                               const GValue *value,
494                               GParamSpec   *pspec)
495 {
496   GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
497
498   switch (prop_id)
499     {
500     case PROP_PIXBUF:
501       gtk_status_icon_set_from_pixbuf (status_icon, g_value_get_object (value));
502       break;
503     case PROP_FILE:
504       gtk_status_icon_set_from_file (status_icon, g_value_get_string (value));
505       break;
506     case PROP_STOCK:
507       gtk_status_icon_set_from_stock (status_icon, g_value_get_string (value));
508       break;
509     case PROP_ICON_NAME:
510       gtk_status_icon_set_from_icon_name (status_icon, g_value_get_string (value));
511       break;
512     case PROP_BLINKING:
513       gtk_status_icon_set_blinking (status_icon, g_value_get_boolean (value));
514       break;
515     case PROP_VISIBLE:
516       gtk_status_icon_set_visible (status_icon, g_value_get_boolean (value));
517       break;
518     default:
519       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520       break;
521     }
522 }
523
524 static void
525 gtk_status_icon_get_property (GObject    *object,
526                               guint       prop_id,
527                               GValue     *value,
528                               GParamSpec *pspec)
529 {
530   GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
531   GtkStatusIconPrivate *priv = status_icon->priv;
532
533   /* The "getter" functions whine if you try to get the wrong
534    * storage type. This function is instead robust against that,
535    * so that GUI builders don't have to jump through hoops
536    * to avoid g_warning
537    */
538
539   switch (prop_id)
540     {
541     case PROP_PIXBUF:
542       if (priv->storage_type != GTK_IMAGE_PIXBUF)
543         g_value_set_object (value, NULL);
544       else
545         g_value_set_object (value, gtk_status_icon_get_pixbuf (status_icon));
546       break;
547     case PROP_STOCK:
548       if (priv->storage_type != GTK_IMAGE_STOCK)
549         g_value_set_string (value, NULL);
550       else
551         g_value_set_string (value, gtk_status_icon_get_stock (status_icon));
552       break;
553     case PROP_ICON_NAME:
554       if (priv->storage_type != GTK_IMAGE_ICON_NAME)
555         g_value_set_string (value, NULL);
556       else
557         g_value_set_string (value, gtk_status_icon_get_icon_name (status_icon));
558       break;
559     case PROP_STORAGE_TYPE:
560       g_value_set_enum (value, gtk_status_icon_get_storage_type (status_icon));
561       break;
562     case PROP_SIZE:
563       g_value_set_int (value, gtk_status_icon_get_size (status_icon));
564       break;
565     case PROP_BLINKING:
566       g_value_set_boolean (value, gtk_status_icon_get_blinking (status_icon));
567       break;
568     case PROP_VISIBLE:
569       g_value_set_boolean (value, gtk_status_icon_get_visible (status_icon));
570       break;
571     default:
572       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
573       break;
574     }
575 }
576
577 /**
578  * gtk_status_icon_new:
579  * 
580  * Creates an empty status icon object.
581  * 
582  * Return value: a new #GtkStatusIcon
583  *
584  * Since: 2.10
585  **/
586 GtkStatusIcon *
587 gtk_status_icon_new (void)
588 {
589   return g_object_new (GTK_TYPE_STATUS_ICON, NULL);
590 }
591
592 /**
593  * gtk_status_icon_new_from_pixbuf:
594  * @pixbuf: a #GdkPixbuf
595  * 
596  * Creates a status icon displaying @pixbuf. 
597  *
598  * The image will be scaled down to fit in the available 
599  * space in the notification area, if necessary.
600  * 
601  * Return value: a new #GtkStatusIcon
602  *
603  * Since: 2.10
604  **/
605 GtkStatusIcon *
606 gtk_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf)
607 {
608   return g_object_new (GTK_TYPE_STATUS_ICON,
609                        "pixbuf", pixbuf,
610                        NULL);
611 }
612
613 /**
614  * gtk_status_icon_new_from_file:
615  * @filename: a filename
616  * 
617  * Creates a status icon displaying the file @filename. 
618  *
619  * The image will be scaled down to fit in the available 
620  * space in the notification area, if necessary.
621  * 
622  * Return value: a new #GtkStatusIcon
623  *
624  * Since: 2.10
625  **/
626 GtkStatusIcon *
627 gtk_status_icon_new_from_file (const gchar *filename)
628 {
629   return g_object_new (GTK_TYPE_STATUS_ICON,
630                        "file", filename,
631                        NULL);
632 }
633
634 /**
635  * gtk_status_icon_new_from_stock:
636  * @stock_id: a stock icon id
637  * 
638  * Creates a status icon displaying a stock icon. Sample stock icon
639  * names are #GTK_STOCK_OPEN, #GTK_STOCK_QUIT. You can register your 
640  * own stock icon names, see gtk_icon_factory_add_default() and 
641  * gtk_icon_factory_add(). 
642  *
643  * Return value: a new #GtkStatusIcon
644  *
645  * Since: 2.10
646  **/
647 GtkStatusIcon *
648 gtk_status_icon_new_from_stock (const gchar *stock_id)
649 {
650   return g_object_new (GTK_TYPE_STATUS_ICON,
651                        "stock", stock_id,
652                        NULL);
653 }
654
655 /**
656  * gtk_status_icon_new_from_icon_name:
657  * @icon_name: an icon name
658  * 
659  * Creates a status icon displaying an icon from the current icon theme.
660  * If the current icon theme is changed, the icon will be updated 
661  * appropriately.
662  * 
663  * Return value: a new #GtkStatusIcon
664  *
665  * Since: 2.10
666  **/
667 GtkStatusIcon *
668 gtk_status_icon_new_from_icon_name (const gchar *icon_name)
669 {
670   return g_object_new (GTK_TYPE_STATUS_ICON,
671                        "icon-name", icon_name,
672                        NULL);
673 }
674
675 static void
676 emit_activate_signal (GtkStatusIcon *status_icon)
677 {
678   g_signal_emit (status_icon,
679                  status_icon_signals [ACTIVATE_SIGNAL], 0);
680 }
681
682 static void
683 emit_popup_menu_signal (GtkStatusIcon *status_icon,
684                         guint          button,
685                         guint32        activate_time)
686 {
687   g_signal_emit (status_icon,
688                  status_icon_signals [POPUP_MENU_SIGNAL], 0,
689                  button,
690                  activate_time);
691 }
692
693 #ifdef GDK_WINDOWING_X11
694
695 static gboolean
696 emit_size_changed_signal (GtkStatusIcon *status_icon,
697                           gint           size)
698 {
699   gboolean handled = FALSE;
700   
701   g_signal_emit (status_icon,
702                  status_icon_signals [SIZE_CHANGED_SIGNAL], 0,
703                  size,
704                  &handled);
705
706   return handled;
707 }
708
709 #endif
710
711 static GdkPixbuf *
712 gtk_status_icon_blank_icon (GtkStatusIcon *status_icon)
713 {
714   GtkStatusIconPrivate *priv = status_icon->priv;
715
716   if (priv->blank_icon)
717     {
718       gint width, height;
719
720       width  = gdk_pixbuf_get_width (priv->blank_icon);
721       height = gdk_pixbuf_get_height (priv->blank_icon);
722
723
724       if (width == priv->image_width && height == priv->image_height)
725         return priv->blank_icon;
726       else
727         {
728           g_object_unref (priv->blank_icon);
729           priv->blank_icon = NULL;
730         }
731     }
732
733   priv->blank_icon = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
734                                      priv->image_width, 
735                                      priv->image_height);
736   if (priv->blank_icon)
737     gdk_pixbuf_fill (priv->blank_icon, 0);
738
739   return priv->blank_icon;
740 }
741
742 #ifdef GDK_WINDOWING_X11
743
744 static GtkIconSize
745 find_icon_size (GtkWidget *widget, 
746                 gint       pixel_size)
747 {
748   GdkScreen *screen;
749   GtkSettings *settings;
750   GtkIconSize s, size;
751   gint w, h, d, dist;
752
753   screen = gtk_widget_get_screen (widget);
754
755   if (!screen)
756     return GTK_ICON_SIZE_MENU;
757
758   settings = gtk_settings_get_for_screen (screen);
759   
760   dist = G_MAXINT;
761   size = GTK_ICON_SIZE_MENU;
762
763   for (s = GTK_ICON_SIZE_MENU; s < GTK_ICON_SIZE_DIALOG; s++)
764     {
765       if (gtk_icon_size_lookup_for_settings (settings, s, &w, &h) &&
766           w <= pixel_size && h <= pixel_size)
767         {
768           d = MAX (pixel_size - w, pixel_size - h);
769           if (d < dist)
770             {
771               dist = d;
772               size = s;
773             }
774         }
775     }
776   
777   return size;
778 }
779
780 #endif
781
782 static void
783 gtk_status_icon_update_image (GtkStatusIcon *status_icon)
784 {
785   GtkStatusIconPrivate *priv = status_icon->priv;
786
787   if (priv->blink_off)
788     {
789 #ifdef GDK_WINDOWING_X11
790       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image),
791                                  gtk_status_icon_blank_icon (status_icon));
792 #endif
793 #ifdef GDK_WINDOWING_WIN32
794       priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (gtk_status_icon_blank_icon (status_icon));
795       priv->nid.uFlags |= NIF_ICON;
796       if (priv->nid.hWnd != NULL && priv->visible)
797         if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
798           g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
799 #endif
800       return;
801     }
802
803   switch (priv->storage_type)
804     {
805     case GTK_IMAGE_PIXBUF:
806       {
807         GdkPixbuf *pixbuf;
808
809         pixbuf = priv->image_data.pixbuf;
810
811         if (pixbuf)
812           {
813             GdkPixbuf *scaled;
814             gint size;
815             gint width;
816             gint height;
817
818             size = priv->size;
819
820             width  = gdk_pixbuf_get_width  (pixbuf);
821             height = gdk_pixbuf_get_height (pixbuf);
822
823             if (width > size || height > size)
824               scaled = gdk_pixbuf_scale_simple (pixbuf,
825                                                 MIN (size, width),
826                                                 MIN (size, height),
827                                                 GDK_INTERP_BILINEAR);
828             else
829               scaled = g_object_ref (pixbuf);
830
831 #ifdef GDK_WINDOWING_X11
832             gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), scaled);
833 #endif
834 #ifdef GDK_WINDOWING_WIN32
835             priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (scaled);
836             priv->nid.uFlags |= NIF_ICON;
837             if (priv->nid.hWnd != NULL && priv->visible)
838               if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
839                   g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
840 #endif
841             g_object_unref (scaled);
842           }
843         else
844           {
845 #ifdef GDK_WINDOWING_X11
846             gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
847 #endif
848 #ifdef GDK_WINDOWING_WIN32
849             priv->nid.uFlags &= ~NIF_ICON;
850             if (priv->nid.hWnd != NULL && priv->visible)
851               if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
852                 g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
853 #endif
854           }
855       }
856       break;
857
858     case GTK_IMAGE_STOCK:
859       {
860 #ifdef GDK_WINDOWING_X11
861         GtkIconSize size = find_icon_size (priv->image, priv->size);
862         gtk_image_set_from_stock (GTK_IMAGE (priv->image),
863                                   priv->image_data.stock_id,
864                                   size);
865 #endif
866 #ifdef GDK_WINDOWING_WIN32
867         {
868           GdkPixbuf *pixbuf =
869             gtk_widget_render_icon (priv->dummy_widget,
870                                     priv->image_data.stock_id,
871                                     GTK_ICON_SIZE_SMALL_TOOLBAR,
872                                     NULL);
873           priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (pixbuf);
874           priv->nid.uFlags |= NIF_ICON;
875           if (priv->nid.hWnd != NULL && priv->visible)
876             if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
877               g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
878           g_object_unref (pixbuf);
879         }
880 #endif
881       }
882       break;
883       
884     case GTK_IMAGE_ICON_NAME:
885       {
886 #ifdef GDK_WINDOWING_X11
887         GtkIconSize size = find_icon_size (priv->image, priv->size);
888         gtk_image_set_from_icon_name (GTK_IMAGE (priv->image),
889                                       priv->image_data.icon_name,
890                                       size);
891 #endif
892 #ifdef GDK_WINDOWING_WIN32
893         {
894           GdkPixbuf *pixbuf =
895             gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
896                                       priv->image_data.icon_name,
897                                       priv->size,
898                                       0, NULL);
899           
900           priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (pixbuf);
901           priv->nid.uFlags |= NIF_ICON;
902           if (priv->nid.hWnd != NULL && priv->visible)
903             if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
904               g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
905           g_object_unref (pixbuf);
906         }
907 #endif
908       }
909       break;
910       
911     case GTK_IMAGE_EMPTY:
912 #ifdef GDK_WINDOWING_X11
913       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
914 #endif
915 #ifdef GDK_WINDOWING_WIN32
916       priv->nid.uFlags &= ~NIF_ICON;
917       if (priv->nid.hWnd != NULL && priv->visible)
918         if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
919           g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
920 #endif
921       break;
922     default:
923       g_assert_not_reached ();
924       break;
925     }
926 }
927
928 #ifdef GDK_WINDOWING_X11
929
930 static void
931 gtk_status_icon_size_allocate (GtkStatusIcon *status_icon,
932                                GtkAllocation *allocation)
933 {
934   GtkStatusIconPrivate *priv = status_icon->priv;
935   GtkOrientation orientation;
936   gint size;
937
938   orientation = _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (priv->tray_icon));
939
940   if (orientation == GTK_ORIENTATION_HORIZONTAL)
941     size = allocation->height;
942   else
943     size = allocation->width;
944
945   priv->image_width = allocation->width - GTK_MISC (priv->image)->xpad * 2;
946   priv->image_height = allocation->height - GTK_MISC (priv->image)->ypad * 2;
947
948   if (priv->size != size)
949     {
950       priv->size = size;
951
952       g_object_notify (G_OBJECT (status_icon), "size");
953
954       if (!emit_size_changed_signal (status_icon, size))
955         gtk_status_icon_update_image (status_icon);
956     }
957 }
958
959 #endif
960
961 static gboolean
962 gtk_status_icon_button_press (GtkStatusIcon  *status_icon,
963                               GdkEventButton *event)
964 {
965   if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
966     {
967       emit_activate_signal (status_icon);
968       return TRUE;
969     }
970   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
971     {
972       emit_popup_menu_signal (status_icon, event->button, event->time);
973       return TRUE;
974     }
975
976   return FALSE;
977 }
978
979 static void
980 gtk_status_icon_reset_image_data (GtkStatusIcon *status_icon)
981 {
982   GtkStatusIconPrivate *priv = status_icon->priv;
983
984   priv->storage_type = GTK_IMAGE_EMPTY;
985   g_object_notify (G_OBJECT (status_icon), "storage-type");
986
987   switch (priv->storage_type)
988   {
989     case GTK_IMAGE_PIXBUF:
990       if (priv->image_data.pixbuf)
991         g_object_unref (priv->image_data.pixbuf);
992       priv->image_data.pixbuf = NULL;
993       g_object_notify (G_OBJECT (status_icon), "pixbuf");
994       break;
995
996     case GTK_IMAGE_STOCK:
997       g_free (priv->image_data.stock_id);
998       priv->image_data.stock_id = NULL;
999
1000       g_object_notify (G_OBJECT (status_icon), "stock");
1001       break;
1002       
1003     case GTK_IMAGE_ICON_NAME:
1004       g_free (priv->image_data.icon_name);
1005       priv->image_data.icon_name = NULL;
1006
1007       g_object_notify (G_OBJECT (status_icon), "icon-name");
1008       break;
1009       
1010     case GTK_IMAGE_EMPTY:
1011       break;
1012     default:
1013       g_assert_not_reached ();
1014       break;
1015   }
1016 }
1017
1018 static void
1019 gtk_status_icon_set_image (GtkStatusIcon *status_icon,
1020                            GtkImageType   storage_type,
1021                            gpointer       data)
1022 {
1023   GtkStatusIconPrivate *priv = status_icon->priv;
1024
1025   g_object_freeze_notify (G_OBJECT (status_icon));
1026
1027   gtk_status_icon_reset_image_data (status_icon);
1028
1029   priv->storage_type = storage_type;
1030   g_object_notify (G_OBJECT (status_icon), "storage-type");
1031
1032   switch (storage_type) 
1033     {
1034     case GTK_IMAGE_PIXBUF:
1035       priv->image_data.pixbuf = (GdkPixbuf *)data;
1036       g_object_notify (G_OBJECT (status_icon), "pixbuf");
1037       break;
1038     case GTK_IMAGE_STOCK:
1039       priv->image_data.stock_id = g_strdup ((const gchar *)data);
1040       g_object_notify (G_OBJECT (status_icon), "stock");
1041       break;
1042     case GTK_IMAGE_ICON_NAME:
1043       priv->image_data.icon_name = g_strdup ((const gchar *)data);
1044       g_object_notify (G_OBJECT (status_icon), "icon-name");
1045       break;
1046     default:
1047       g_warning ("Image type %d not handled by GtkStatusIcon", storage_type);
1048     }
1049
1050   g_object_thaw_notify (G_OBJECT (status_icon));
1051
1052   gtk_status_icon_update_image (status_icon);
1053 }
1054
1055 /**
1056  * gtk_status_icon_set_from_pixbuf:
1057  * @status_icon: a #GtkStatusIcon
1058  * @pixbuf: a #GdkPixbuf or %NULL
1059  * 
1060  * Makes @status_icon display @pixbuf. 
1061  * See gtk_status_icon_new_from_pixbuf() for details.
1062  *
1063  * Since: 2.10
1064  **/
1065 void
1066 gtk_status_icon_set_from_pixbuf (GtkStatusIcon *status_icon,
1067                                  GdkPixbuf     *pixbuf)
1068 {
1069   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1070   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
1071
1072   if (pixbuf)
1073     g_object_ref (pixbuf);
1074
1075   gtk_status_icon_set_image (status_icon, GTK_IMAGE_PIXBUF,
1076                              (gpointer) pixbuf);
1077 }
1078
1079 /**
1080  * gtk_status_icon_set_from_file:
1081  * @status_icon: a #GtkStatusIcon
1082  * @filename: a filename
1083  * 
1084  * Makes @status_icon display the file @filename.
1085  * See gtk_status_icon_new_from_file() for details.
1086  *
1087  * Since: 2.10 
1088  **/
1089 void
1090 gtk_status_icon_set_from_file (GtkStatusIcon *status_icon,
1091                                const gchar   *filename)
1092 {
1093   GdkPixbuf *pixbuf;
1094   
1095   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1096   g_return_if_fail (filename != NULL);
1097   
1098   pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1099   
1100   gtk_status_icon_set_from_pixbuf (status_icon, pixbuf);
1101   
1102   if (pixbuf)
1103     g_object_unref (pixbuf);
1104 }
1105
1106 /**
1107  * gtk_status_icon_set_from_stock:
1108  * @status_icon: a #GtkStatusIcon
1109  * @stock_id: a stock icon id
1110  * 
1111  * Makes @status_icon display the stock icon with the id @stock_id.
1112  * See gtk_status_icon_new_from_stock() for details.
1113  *
1114  * Since: 2.10 
1115  **/
1116 void
1117 gtk_status_icon_set_from_stock (GtkStatusIcon *status_icon,
1118                                 const gchar   *stock_id)
1119 {
1120   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1121   g_return_if_fail (stock_id != NULL);
1122
1123   gtk_status_icon_set_image (status_icon, GTK_IMAGE_STOCK,
1124                              (gpointer) stock_id);
1125 }
1126
1127 /**
1128  * gtk_status_icon_set_from_icon_name:
1129  * @status_icon: a #GtkStatusIcon
1130  * @icon_name: an icon name
1131  * 
1132  * Makes @status_icon display the icon named @icon_name from the 
1133  * current icon theme.
1134  * See gtk_status_icon_new_from_icon_name() for details.
1135  *
1136  * Since: 2.10 
1137  **/
1138 void
1139 gtk_status_icon_set_from_icon_name (GtkStatusIcon *status_icon,
1140                                     const gchar   *icon_name)
1141 {
1142   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1143   g_return_if_fail (icon_name != NULL);
1144
1145   gtk_status_icon_set_image (status_icon, GTK_IMAGE_ICON_NAME,
1146                              (gpointer) icon_name);
1147 }
1148
1149 /**
1150  * gtk_status_icon_get_storage_type:
1151  * @status_icon: a #GtkStatusIcon
1152  * 
1153  * Gets the type of representation being used by the #GtkStatusIcon
1154  * to store image data. If the #GtkStatusIcon has no image data,
1155  * the return value will be %GTK_IMAGE_EMPTY. 
1156  * 
1157  * Return value: the image representation being used
1158  *
1159  * Since: 2.10
1160  **/
1161 GtkImageType
1162 gtk_status_icon_get_storage_type (GtkStatusIcon *status_icon)
1163 {
1164   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), GTK_IMAGE_EMPTY);
1165
1166   return status_icon->priv->storage_type;
1167 }
1168 /**
1169  * gtk_status_icon_get_pixbuf:
1170  * @status_icon: a #GtkStatusIcon
1171  * 
1172  * Gets the #GdkPixbuf being displayed by the #GtkStatusIcon.
1173  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
1174  * %GTK_IMAGE_PIXBUF (see gtk_status_icon_get_storage_type()).
1175  * The caller of this function does not own a reference to the
1176  * returned pixbuf.
1177  * 
1178  * Return value: the displayed pixbuf, or %NULL if the image is empty.
1179  *
1180  * Since: 2.10
1181  **/
1182 GdkPixbuf *
1183 gtk_status_icon_get_pixbuf (GtkStatusIcon *status_icon)
1184 {
1185   GtkStatusIconPrivate *priv;
1186
1187   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
1188
1189   priv = status_icon->priv;
1190
1191   g_return_val_if_fail (priv->storage_type == GTK_IMAGE_PIXBUF ||
1192                         priv->storage_type == GTK_IMAGE_EMPTY, NULL);
1193
1194   if (priv->storage_type == GTK_IMAGE_EMPTY)
1195     priv->image_data.pixbuf = NULL;
1196
1197   return priv->image_data.pixbuf;
1198 }
1199
1200 /**
1201  * gtk_status_icon_get_stock:
1202  * @status_icon: a #GtkStatusIcon
1203  * 
1204  * Gets the id of the stock icon being displayed by the #GtkStatusIcon.
1205  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
1206  * %GTK_IMAGE_STOCK (see gtk_status_icon_get_storage_type()).
1207  * The returned string is owned by the #GtkStatusIcon and should not
1208  * be freed or modified.
1209  * 
1210  * Return value: stock id of the displayed stock icon,
1211  *   or %NULL if the image is empty.
1212  *
1213  * Since: 2.10
1214  **/
1215 G_CONST_RETURN gchar *
1216 gtk_status_icon_get_stock (GtkStatusIcon *status_icon)
1217 {
1218   GtkStatusIconPrivate *priv;
1219
1220   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
1221
1222   priv = status_icon->priv;
1223
1224   g_return_val_if_fail (priv->storage_type == GTK_IMAGE_STOCK ||
1225                         priv->storage_type == GTK_IMAGE_EMPTY, NULL);
1226   
1227   if (priv->storage_type == GTK_IMAGE_EMPTY)
1228     priv->image_data.stock_id = NULL;
1229
1230   return priv->image_data.stock_id;
1231 }
1232
1233 /**
1234  * gtk_status_icon_get_icon_name:
1235  * @status_icon: a #GtkStatusIcon
1236  * 
1237  * Gets the name of the icon being displayed by the #GtkStatusIcon.
1238  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
1239  * %GTK_IMAGE_ICON_NAME (see gtk_status_icon_get_storage_type()).
1240  * The returned string is owned by the #GtkStatusIcon and should not
1241  * be freed or modified.
1242  * 
1243  * Return value: name of the displayed icon, or %NULL if the image is empty.
1244  *
1245  * Since: 2.10
1246  **/
1247 G_CONST_RETURN gchar *
1248 gtk_status_icon_get_icon_name (GtkStatusIcon *status_icon)
1249 {
1250   GtkStatusIconPrivate *priv;
1251   
1252   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
1253
1254   priv = status_icon->priv;
1255
1256   g_return_val_if_fail (priv->storage_type == GTK_IMAGE_ICON_NAME ||
1257                         priv->storage_type == GTK_IMAGE_EMPTY, NULL);
1258
1259   if (priv->storage_type == GTK_IMAGE_EMPTY)
1260     priv->image_data.icon_name = NULL;
1261
1262   return priv->image_data.icon_name;
1263 }
1264
1265 /**
1266  * gtk_status_icon_get_size:
1267  * @status_icon: a #GtkStatusIcon
1268  * 
1269  * Gets the size in pixels that is available for the image. 
1270  * Stock icons and named icons adapt their size automatically
1271  * if the size of the notification area changes. For other
1272  * storage types, the size-changed signal can be used to
1273  * react to size changes.
1274  * 
1275  * Return value: the size that is available for the image
1276  *
1277  * Since: 2.10
1278  **/
1279 gint
1280 gtk_status_icon_get_size (GtkStatusIcon *status_icon)
1281 {
1282   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), 0);
1283
1284   return status_icon->priv->size;
1285 }
1286
1287 /**
1288  * gtk_status_icon_set_tooltip:
1289  * @status_icon: a #GtkStatusIcon
1290  * @tooltip_text: the tooltip text, or %NULL
1291  * 
1292  * Sets the tooltip of the status icon.
1293  * 
1294  * Since: 2.10
1295  **/ 
1296 void
1297 gtk_status_icon_set_tooltip (GtkStatusIcon *status_icon,
1298                              const gchar   *tooltip_text)
1299 {
1300   GtkStatusIconPrivate *priv;
1301
1302   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1303
1304   priv = status_icon->priv;
1305
1306 #ifdef GDK_WINDOWING_X11
1307   gtk_tooltips_set_tip (priv->tooltips, priv->tray_icon,
1308                         tooltip_text, NULL);
1309 #endif
1310 #ifdef GDK_WINDOWING_WIN32
1311   if (tooltip_text == NULL)
1312     priv->nid.uFlags &= ~NIF_TIP;
1313   else
1314     {
1315       WCHAR *wcs = g_utf8_to_utf16 (tooltip_text, -1, NULL, NULL, NULL);
1316
1317       priv->nid.uFlags |= NIF_TIP;
1318       wcsncpy (priv->nid.szTip, wcs, G_N_ELEMENTS (priv->nid.szTip) - 1);
1319       priv->nid.szTip[G_N_ELEMENTS (priv->nid.szTip) - 1] = 0;
1320       g_free (wcs);
1321     }
1322   if (priv->nid.hWnd != NULL && priv->visible)
1323     if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
1324       g_warning ("%s:%d:Shell_NotifyIconW(NIM_MODIFY) failed", __FILE__, __LINE__-1);
1325 #endif
1326 }
1327
1328 static gboolean
1329 gtk_status_icon_blinker (GtkStatusIcon *status_icon)
1330 {
1331   GtkStatusIconPrivate *priv = status_icon->priv;
1332   
1333   priv->blink_off = !priv->blink_off;
1334
1335   gtk_status_icon_update_image (status_icon);
1336
1337   return TRUE;
1338 }
1339
1340 static void
1341 gtk_status_icon_enable_blinking (GtkStatusIcon *status_icon)
1342 {
1343   GtkStatusIconPrivate *priv = status_icon->priv;
1344   
1345   if (!priv->blinking_timeout)
1346     {
1347       gtk_status_icon_blinker (status_icon);
1348
1349       priv->blinking_timeout =
1350         g_timeout_add (BLINK_TIMEOUT, 
1351                        (GSourceFunc) gtk_status_icon_blinker, 
1352                        status_icon);
1353     }
1354 }
1355
1356 static void
1357 gtk_status_icon_disable_blinking (GtkStatusIcon *status_icon)
1358 {
1359   GtkStatusIconPrivate *priv = status_icon->priv;
1360   
1361   if (priv->blinking_timeout)
1362     {
1363       g_source_remove (priv->blinking_timeout);
1364       priv->blinking_timeout = 0;
1365       priv->blink_off = FALSE;
1366
1367       gtk_status_icon_update_image (status_icon);
1368     }
1369 }
1370
1371 /**
1372  * gtk_status_icon_set_visible:
1373  * @status_icon: a #GtkStatusIcon
1374  * @visible: %TRUE to show the status icon, %FALSE to hide it
1375  * 
1376  * Shows or hides a status icon.
1377  *
1378  * Since: 2.10
1379  **/
1380 void
1381 gtk_status_icon_set_visible (GtkStatusIcon *status_icon,
1382                              gboolean       visible)
1383 {
1384   GtkStatusIconPrivate *priv;
1385
1386   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1387
1388   priv = status_icon->priv;
1389
1390   visible = visible != FALSE;
1391
1392   if (priv->visible != visible)
1393     {
1394       priv->visible = visible;
1395
1396 #ifdef GDK_WINDOWING_X11
1397       if (visible)
1398         gtk_widget_show (priv->tray_icon);
1399       else
1400         gtk_widget_hide (priv->tray_icon);
1401 #endif
1402 #ifdef GDK_WINDOWING_WIN32
1403       if (priv->nid.hWnd != NULL)
1404         {
1405           if (visible)
1406             Shell_NotifyIconW (NIM_ADD, &priv->nid);
1407           else
1408             Shell_NotifyIconW (NIM_DELETE, &priv->nid);
1409         }
1410 #endif
1411       g_object_notify (G_OBJECT (status_icon), "visible");
1412     }
1413 }
1414
1415 /**
1416  * gtk_status_icon_get_visible:
1417  * @status_icon: a #GtkStatusIcon
1418  * 
1419  * Returns whether the status icon is visible or not. 
1420  * Note that being visible does not guarantee that 
1421  * the user can actually see the icon, see also 
1422  * gtk_status_icon_is_embedded().
1423  * 
1424  * Return value: %TRUE if the status icon is visible
1425  *
1426  * Since: 2.10
1427  **/
1428 gboolean
1429 gtk_status_icon_get_visible (GtkStatusIcon *status_icon)
1430 {
1431   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
1432
1433   return status_icon->priv->visible;
1434 }
1435
1436 /**
1437  * gtk_status_icon_set_blinking:
1438  * @status_icon: a #GtkStatusIcon
1439  * @blinking: %TRUE to turn blinking on, %FALSE to turn it off
1440  * 
1441  * Makes the status icon start or stop blinking. 
1442  * Note that blinking user interface elements may be problematic
1443  * for some users, and thus may be turned off, in which case
1444  * this setting has no effect.
1445  *
1446  * Since: 2.10
1447  **/
1448 void
1449 gtk_status_icon_set_blinking (GtkStatusIcon *status_icon,
1450                               gboolean       blinking)
1451 {
1452   GtkStatusIconPrivate *priv;
1453
1454   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1455
1456   priv = status_icon->priv;
1457
1458   blinking = blinking != FALSE;
1459
1460   if (priv->blinking != blinking)
1461     {
1462       priv->blinking = blinking;
1463
1464       if (blinking)
1465         gtk_status_icon_enable_blinking (status_icon);
1466       else
1467         gtk_status_icon_disable_blinking (status_icon);
1468
1469       g_object_notify (G_OBJECT (status_icon), "blinking");
1470     }
1471 }
1472
1473 /**
1474  * gtk_status_icon_get_blinking:
1475  * @status_icon: a #GtkStatusIcon
1476  * 
1477  * Returns whether the icon is blinking, see 
1478  * gtk_status_icon_set_blinking().
1479  * 
1480  * Return value: %TRUE if the icon is blinking
1481  *
1482  * Since: 2.10
1483  **/
1484 gboolean
1485 gtk_status_icon_get_blinking (GtkStatusIcon *status_icon)
1486 {
1487   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
1488
1489   return status_icon->priv->blinking;
1490 }
1491
1492 /**
1493  * gtk_status_icon_is_embedded:
1494  * @status_icon: a #GtkStatusIcon
1495  * 
1496  * Returns whether the status icon is embedded in a notification
1497  * area. 
1498  * 
1499  * Return value: %TRUE if the status icon is embedded in
1500  *   a notification area.
1501  *
1502  * Since: 2.10
1503  **/
1504 gboolean
1505 gtk_status_icon_is_embedded (GtkStatusIcon *status_icon)
1506 {
1507 #ifdef GDK_WINDOWING_X11
1508   GtkPlug *plug;
1509 #endif
1510
1511   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
1512
1513 #ifdef GDK_WINDOWING_X11
1514   plug = GTK_PLUG (status_icon->priv->tray_icon);
1515
1516   if (plug->socket_window)
1517     return TRUE;
1518   else
1519     return FALSE;
1520 #endif
1521 #ifdef GDK_WINDOWING_WIN32
1522   return TRUE;
1523 #endif
1524 }
1525
1526 /**
1527  * gtk_status_icon_position_menu:
1528  * @menu: the #GtkMenu
1529  * @x: return location for the x position
1530  * @y: return location for the y position
1531  * @push_in: return location for whether the menu should be pushed in 
1532  *     to be completely inside the screen instead of just clamped to the 
1533  *     size to the screen.
1534  * @user_data: the status icon to position the menu on
1535  *
1536  * Menu positioning function to use with gtk_menu_popup()
1537  * to position @menu aligned to the status icon @user_data.
1538  * 
1539  * Since: 2.10
1540  **/
1541 void
1542 gtk_status_icon_position_menu (GtkMenu  *menu,
1543                                gint     *x,
1544                                gint     *y,
1545                                gboolean *push_in,
1546                                gpointer  user_data)
1547 {
1548 #ifdef GDK_WINDOWING_X11
1549   GtkStatusIcon *status_icon;
1550   GtkStatusIconPrivate *priv;
1551   GtkTrayIcon *tray_icon;
1552   GtkWidget *widget;
1553   GdkScreen *screen;
1554   GtkTextDirection direction;
1555   GtkRequisition menu_req;
1556   GdkRectangle monitor;
1557   gint monitor_num, height, width, xoffset, yoffset;
1558   
1559   g_return_if_fail (GTK_IS_MENU (menu));
1560   g_return_if_fail (GTK_IS_STATUS_ICON (user_data));
1561
1562   status_icon = GTK_STATUS_ICON (user_data);
1563   priv = status_icon->priv;
1564   tray_icon = GTK_TRAY_ICON (priv->tray_icon);
1565   widget = priv->tray_icon;
1566
1567   direction = gtk_widget_get_direction (widget);
1568
1569   screen = gtk_widget_get_screen (widget);
1570   gtk_menu_set_screen (menu, screen);
1571
1572   monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
1573   if (monitor_num < 0)
1574     monitor_num = 0;
1575   gtk_menu_set_monitor (menu, monitor_num);
1576
1577   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1578
1579   gdk_window_get_origin (widget->window, x, y);
1580   
1581   gtk_widget_size_request (GTK_WIDGET (menu), &menu_req);
1582
1583   if (_gtk_tray_icon_get_orientation (tray_icon) == GTK_ORIENTATION_VERTICAL)
1584     {
1585       width = 0;
1586       height = widget->allocation.height;
1587       xoffset = widget->allocation.width;
1588       yoffset = 0;
1589     }
1590   else
1591     {
1592       width = widget->allocation.width;
1593       height = 0;
1594       xoffset = 0;
1595       yoffset = widget->allocation.height;
1596     }
1597
1598   if (direction == GTK_TEXT_DIR_RTL)
1599     {
1600       if ((*x - (menu_req.width - width)) >= monitor.x)
1601         *x -= menu_req.width - width;
1602       else if ((*x + xoffset + menu_req.width) < (monitor.x + monitor.width))
1603         *x += xoffset;
1604       else if ((monitor.x + monitor.width - (*x + xoffset)) < *x)
1605         *x -= menu_req.width - width;
1606       else
1607         *x += xoffset;
1608     }
1609   else
1610     {
1611       if ((*x + xoffset + menu_req.width) < (monitor.x + monitor.width))
1612         *x += xoffset;
1613       else if ((*x - (menu_req.width - width)) >= monitor.x)
1614         *x -= menu_req.width - width;
1615       else if ((monitor.x + monitor.width - (*x + xoffset)) > *x)
1616         *x += xoffset;
1617       else 
1618         *x -= menu_req.width - width;
1619     }
1620
1621   if ((*y + yoffset + menu_req.height) < (monitor.y + monitor.height))
1622     *y += yoffset;
1623   else if ((*y - (menu_req.height - height)) >= monitor.y)
1624     *y -= menu_req.height - height;
1625   else if (monitor.y + monitor.height - (*y + yoffset) > *y)
1626     *y += yoffset;
1627   else 
1628     *y -= menu_req.height - height;
1629
1630   *push_in = FALSE;
1631 #endif /* GDK_WINDOWING_X11 */
1632 }
1633
1634 /**
1635  * gtk_status_icon_get_geometry:
1636  * @status_icon: a #GtkStatusIcon
1637  * @screen: return location for the screen 
1638  * @area: return location for the area occupied by the status icon
1639  * @orientation: return location for the orientation of the panel 
1640  *    in which the status icon is embedded. A panel at the top or
1641  *    bottom of the screen is horizontal, a panel at the left or
1642  *    right is vertical.
1643  *
1644  * Obtains information about the location of the status icon
1645  * on screen. This information can be used to e.g. position 
1646  * popups like notification bubbles. 
1647  * See gtk_status_icon_position_menu() for a more convenient 
1648  * alternative for positioning menus.
1649  *
1650  * Note that some platforms do not allow GTK+ to provide 
1651  * this information.
1652  *
1653  * Return value: %TRUE if the location information has 
1654  *               been filled in
1655  *
1656  * Since: 2.10
1657  */
1658 gboolean  
1659 gtk_status_icon_get_geometry (GtkStatusIcon    *status_icon,
1660                               GdkScreen       **screen,
1661                               GdkRectangle     *area,
1662                               GtkOrientation   *orientation)
1663 {
1664 #ifdef GDK_WINDOWING_X11   
1665   GtkWidget *widget;
1666   GtkStatusIconPrivate *priv;
1667   gint x, y;
1668
1669   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1670
1671   priv = status_icon->priv;
1672   widget = priv->tray_icon;
1673
1674   *screen = gtk_widget_get_screen (widget);
1675   gdk_window_get_origin (widget->window, &x, &y);
1676   area->x = x;
1677   area->y = y;
1678   area->width = widget->allocation.width;
1679   area->height = widget->allocation.height;
1680   *orientation = _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (widget));
1681
1682   return TRUE;
1683 #else
1684   return FALSE;
1685 #endif /* GDK_WINDOWING_X11 */
1686 }
1687
1688
1689 #define __GTK_STATUS_ICON_C__
1690 #include "gtkaliasdef.c"