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