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, see <http://www.gnu.org/licenses/>.
20  *
21  * Authors:
22  *	Mark McLoughlin <mark@skynet.ie>
23  *	Hans Breuer <hans@breuer.org>
24  *	Tor Lillqvist <tml@novell.com>
25  *	Mikael Hallendal <micke@imendio.com>
26  */
27 
28 #include "config.h"
29 
30 #include <string.h>
31 #include <stdlib.h>
32 
33 #define GDK_DISABLE_DEPRECATION_WARNINGS
34 #include "gtkstatusicon.h"
35 
36 #include "gtkintl.h"
37 #include "gtkiconhelperprivate.h"
38 #include "gtkmain.h"
39 #include "gtkmarshalers.h"
40 #include "gtksizerequest.h"
41 #include "gtkprivate.h"
42 #include "gtkwidget.h"
43 #include "gtktooltip.h"
44 #include "gtkicontheme.h"
45 #include "gtklabel.h"
46 #include "gtkstylecontextprivate.h"
47 #include "gtktypebuiltins.h"
48 
49 #ifdef GDK_WINDOWING_X11
50 #include "gdk/x11/gdkx.h"
51 #include "gtktrayicon.h"
52 #endif
53 
54 #ifdef GDK_WINDOWING_WIN32
55 #include "gdk/win32/gdkwin32.h"
56 #define WM_GTK_TRAY_NOTIFICATION (WM_USER+1)
57 #endif
58 
59 
60 /**
61  * SECTION:gtkstatusicon
62  * @Short_description: Display an icon in the system tray
63  * @Title: GtkStatusIcon
64  *
65  * The “system tray” or notification area is normally used for transient icons
66  * that indicate some special state. For example, a system tray icon might
67  * appear to tell the user that they have new mail, or have an incoming instant
68  * message, or something along those lines. The basic idea is that creating an
69  * icon in the notification area is less annoying than popping up a dialog.
70  *
71  * A #GtkStatusIcon object can be used to display an icon in a “system tray”.
72  * The icon can have a tooltip, and the user can interact with it by
73  * activating it or popping up a context menu.
74  *
75  * It is very important to notice that status icons depend on the existence
76  * of a notification area being available to the user; you should not use status
77  * icons as the only way to convey critical information regarding your application,
78  * as the notification area may not exist on the user's environment, or may have
79  * been removed. You should always check that a status icon has been embedded into
80  * a notification area by using gtk_status_icon_is_embedded(), and gracefully
81  * recover if the function returns %FALSE.
82  *
83  * On X11, the implementation follows the
84  * [FreeDesktop System Tray Specification](http://www.freedesktop.org/wiki/Specifications/systemtray-spec).
85  * Implementations of the “tray” side of this specification can
86  * be found e.g. in the GNOME 2 and KDE panel applications.
87  *
88  * Note that a GtkStatusIcon is not a widget, but just a #GObject. Making it a
89  * widget would be impractical, since the system tray on Windows doesn’t allow
90  * to embed arbitrary widgets.
91  *
92  * GtkStatusIcon has been deprecated in 3.14. You should consider using
93  * notifications or more modern platform-specific APIs instead. GLib provides
94  * the #GNotification API which works well with #GtkApplication on multiple
95  * platforms and environments, and should be the preferred mechanism to notify
96  * the users of transient status updates. See this [HowDoI](https://wiki.gnome.org/HowDoI/GNotification)
97  * for code examples.
98  */
99 
100 
101 #define BLINK_TIMEOUT 500
102 
103 enum
104 {
105   PROP_0,
106   PROP_PIXBUF,
107   PROP_FILE,
108   PROP_STOCK,
109   PROP_ICON_NAME,
110   PROP_GICON,
111   PROP_STORAGE_TYPE,
112   PROP_SIZE,
113   PROP_SCREEN,
114   PROP_VISIBLE,
115   PROP_ORIENTATION,
116   PROP_EMBEDDED,
117   PROP_BLINKING,
118   PROP_HAS_TOOLTIP,
119   PROP_TOOLTIP_TEXT,
120   PROP_TOOLTIP_MARKUP,
121   PROP_TITLE
122 };
123 
124 enum
125 {
126   ACTIVATE_SIGNAL,
127   POPUP_MENU_SIGNAL,
128   SIZE_CHANGED_SIGNAL,
129   BUTTON_PRESS_EVENT_SIGNAL,
130   BUTTON_RELEASE_EVENT_SIGNAL,
131   SCROLL_EVENT_SIGNAL,
132   QUERY_TOOLTIP_SIGNAL,
133   LAST_SIGNAL
134 };
135 
136 static guint status_icon_signals [LAST_SIGNAL] = { 0 };
137 
138 #ifdef GDK_WINDOWING_QUARTZ
139 #include "gtkstatusicon-quartz.c"
140 #endif
141 
142 struct _GtkStatusIconPrivate
143 {
144 #ifdef GDK_WINDOWING_X11
145   GtkWidget    *tray_icon;
146   GtkWidget    *image;
147 #else
148   GtkWidget    *dummy_widget;
149 #endif
150 
151 #ifdef GDK_WINDOWING_WIN32
152   NOTIFYICONDATAW nid;
153   gint          taskbar_top;
154   gint		last_click_x, last_click_y;
155   GtkOrientation orientation;
156   gchar         *tooltip_text;
157   gchar         *title;
158 #endif
159 
160 #ifdef GDK_WINDOWING_QUARTZ
161   GtkQuartzStatusIcon *status_item;
162   gchar         *tooltip_text;
163   gchar         *title;
164 #endif
165 
166   gint           size;
167   GtkImageDefinition *image_def;
168   guint          visible : 1;
169 };
170 
171 static void     gtk_status_icon_constructed      (GObject        *object);
172 static void     gtk_status_icon_finalize         (GObject        *object);
173 static void     gtk_status_icon_set_property     (GObject        *object,
174 						  guint           prop_id,
175 						  const GValue   *value,
176 						  GParamSpec     *pspec);
177 static void     gtk_status_icon_get_property     (GObject        *object,
178 						  guint           prop_id,
179 						  GValue         *value,
180 					          GParamSpec     *pspec);
181 
182 #ifdef GDK_WINDOWING_X11
183 static void     gtk_status_icon_size_allocate    (GtkStatusIcon  *status_icon,
184 						  GtkAllocation  *allocation);
185 static void     gtk_status_icon_screen_changed   (GtkStatusIcon  *status_icon,
186 						  GdkScreen      *old_screen);
187 static void     gtk_status_icon_embedded_changed (GtkStatusIcon *status_icon);
188 static void     gtk_status_icon_orientation_changed (GtkStatusIcon *status_icon);
189 static void     gtk_status_icon_padding_changed  (GtkStatusIcon *status_icon);
190 static void     gtk_status_icon_icon_size_changed(GtkStatusIcon *status_icon);
191 static void     gtk_status_icon_fg_changed       (GtkStatusIcon *status_icon);
192 static void     gtk_status_icon_color_changed    (GtkTrayIcon   *tray,
193                                                   GParamSpec    *pspec,
194                                                   GtkStatusIcon *status_icon);
195 static gboolean gtk_status_icon_scroll           (GtkStatusIcon  *status_icon,
196 						  GdkEventScroll *event);
197 static gboolean gtk_status_icon_query_tooltip    (GtkStatusIcon *status_icon,
198 						  gint           x,
199 						  gint           y,
200 						  gboolean       keyboard_tip,
201 						  GtkTooltip    *tooltip);
202 
203 static gboolean gtk_status_icon_key_press        (GtkStatusIcon  *status_icon,
204 						  GdkEventKey    *event);
205 static void     gtk_status_icon_popup_menu       (GtkStatusIcon  *status_icon);
206 #endif
207 static gboolean gtk_status_icon_button_press     (GtkStatusIcon  *status_icon,
208 						  GdkEventButton *event);
209 static gboolean gtk_status_icon_button_release   (GtkStatusIcon  *status_icon,
210 						  GdkEventButton *event);
211 static void     gtk_status_icon_reset_image_data (GtkStatusIcon  *status_icon);
212 static void     gtk_status_icon_update_image    (GtkStatusIcon *status_icon);
213 
G_DEFINE_TYPE_WITH_PRIVATE(GtkStatusIcon,gtk_status_icon,G_TYPE_OBJECT)214 G_DEFINE_TYPE_WITH_PRIVATE (GtkStatusIcon, gtk_status_icon, G_TYPE_OBJECT)
215 
216 static void
217 gtk_status_icon_class_init (GtkStatusIconClass *class)
218 {
219   GObjectClass *gobject_class = (GObjectClass *) class;
220 
221   gobject_class->constructed  = gtk_status_icon_constructed;
222   gobject_class->finalize     = gtk_status_icon_finalize;
223   gobject_class->set_property = gtk_status_icon_set_property;
224   gobject_class->get_property = gtk_status_icon_get_property;
225 
226   class->button_press_event   = NULL;
227   class->button_release_event = NULL;
228   class->scroll_event         = NULL;
229   class->query_tooltip        = NULL;
230 
231   g_object_class_install_property (gobject_class,
232 				   PROP_PIXBUF,
233 				   g_param_spec_object ("pixbuf",
234 							P_("Pixbuf"),
235 							P_("A GdkPixbuf to display"),
236 							GDK_TYPE_PIXBUF,
237 							GTK_PARAM_READWRITE));
238 
239   g_object_class_install_property (gobject_class,
240 				   PROP_FILE,
241 				   g_param_spec_string ("file",
242 							P_("Filename"),
243 							P_("Filename to load and display"),
244 							NULL,
245 							GTK_PARAM_WRITABLE));
246 
247   /**
248    * GtkStatusIcon:stock:
249    *
250    * Deprecated: 3.10: Use #GtkStatusIcon:icon-name instead.
251    */
252   g_object_class_install_property (gobject_class,
253 				   PROP_STOCK,
254 				   g_param_spec_string ("stock",
255 							P_("Stock ID"),
256 							P_("Stock ID for a stock image to display"),
257 							NULL,
258 							GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));
259 
260   g_object_class_install_property (gobject_class,
261                                    PROP_ICON_NAME,
262                                    g_param_spec_string ("icon-name",
263                                                         P_("Icon Name"),
264                                                         P_("The name of the icon from the icon theme"),
265                                                         NULL,
266                                                         GTK_PARAM_READWRITE));
267 
268   /**
269    * GtkStatusIcon:gicon:
270    *
271    * The #GIcon displayed in the #GtkStatusIcon. For themed icons,
272    * the image will be updated automatically if the theme changes.
273    *
274    * Since: 2.14
275    */
276   g_object_class_install_property (gobject_class,
277                                    PROP_GICON,
278                                    g_param_spec_object ("gicon",
279                                                         P_("GIcon"),
280                                                         P_("The GIcon being displayed"),
281                                                         G_TYPE_ICON,
282                                                         GTK_PARAM_READWRITE));
283 
284   g_object_class_install_property (gobject_class,
285 				   PROP_STORAGE_TYPE,
286 				   g_param_spec_enum ("storage-type",
287 						      P_("Storage type"),
288 						      P_("The representation being used for image data"),
289 						      GTK_TYPE_IMAGE_TYPE,
290 						      GTK_IMAGE_EMPTY,
291 						      GTK_PARAM_READABLE));
292 
293   g_object_class_install_property (gobject_class,
294 				   PROP_SIZE,
295 				   g_param_spec_int ("size",
296 						     P_("Size"),
297 						     P_("The size of the icon"),
298 						     0,
299 						     G_MAXINT,
300 						     0,
301 						     GTK_PARAM_READABLE));
302 
303   g_object_class_install_property (gobject_class,
304 				   PROP_SCREEN,
305 				   g_param_spec_object ("screen",
306  							P_("Screen"),
307  							P_("The screen where this status icon will be displayed"),
308 							GDK_TYPE_SCREEN,
309  							GTK_PARAM_READWRITE));
310 
311   g_object_class_install_property (gobject_class,
312 				   PROP_VISIBLE,
313 				   g_param_spec_boolean ("visible",
314 							 P_("Visible"),
315 							 P_("Whether the status icon is visible"),
316 							 TRUE,
317 							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
318 
319 
320   /**
321    * GtkStatusIcon:embedded:
322    *
323    * %TRUE if the statusicon is embedded in a notification area.
324    *
325    * Since: 2.12
326    */
327   g_object_class_install_property (gobject_class,
328 				   PROP_EMBEDDED,
329 				   g_param_spec_boolean ("embedded",
330 							 P_("Embedded"),
331 							 P_("Whether the status icon is embedded"),
332 							 FALSE,
333 							 GTK_PARAM_READABLE));
334 
335   /**
336    * GtkStatusIcon:orientation:
337    *
338    * The orientation of the tray in which the statusicon
339    * is embedded.
340    *
341    * Since: 2.12
342    */
343   g_object_class_install_property (gobject_class,
344 				   PROP_ORIENTATION,
345 				   g_param_spec_enum ("orientation",
346 						      P_("Orientation"),
347 						      P_("The orientation of the tray"),
348 						      GTK_TYPE_ORIENTATION,
349 						      GTK_ORIENTATION_HORIZONTAL,
350 						      GTK_PARAM_READABLE));
351 
352 /**
353  * GtkStatusIcon:has-tooltip:
354  *
355  * Enables or disables the emission of #GtkStatusIcon::query-tooltip on
356  * @status_icon.  A value of %TRUE indicates that @status_icon can have a
357  * tooltip, in this case the status icon will be queried using
358  * #GtkStatusIcon::query-tooltip to determine whether it will provide a
359  * tooltip or not.
360  *
361  * Note that setting this property to %TRUE for the first time will change
362  * the event masks of the windows of this status icon to include leave-notify
363  * and motion-notify events. This will not be undone when the property is set
364  * to %FALSE again.
365  *
366  * Whether this property is respected is platform dependent.
367  * For plain text tooltips, use #GtkStatusIcon:tooltip-text in preference.
368  *
369  * Since: 2.16
370  */
371   g_object_class_install_property (gobject_class,
372 				   PROP_HAS_TOOLTIP,
373 				   g_param_spec_boolean ("has-tooltip",
374  							 P_("Has tooltip"),
375  							 P_("Whether this tray icon has a tooltip"),
376  							 FALSE,
377  							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
378 
379   /**
380    * GtkStatusIcon:tooltip-text:
381    *
382    * Sets the text of tooltip to be the given string.
383    *
384    * Also see gtk_tooltip_set_text().
385    *
386    * This is a convenience property which will take care of getting the
387    * tooltip shown if the given string is not %NULL.
388    * #GtkStatusIcon:has-tooltip will automatically be set to %TRUE and
389    * the default handler for the #GtkStatusIcon::query-tooltip signal
390    * will take care of displaying the tooltip.
391    *
392    * Note that some platforms have limitations on the length of tooltips
393    * that they allow on status icons, e.g. Windows only shows the first
394    * 64 characters.
395    *
396    * Since: 2.16
397    */
398   g_object_class_install_property (gobject_class,
399                                    PROP_TOOLTIP_TEXT,
400                                    g_param_spec_string ("tooltip-text",
401                                                         P_("Tooltip Text"),
402                                                         P_("The contents of the tooltip for this widget"),
403                                                         NULL,
404                                                         GTK_PARAM_READWRITE));
405   /**
406    * GtkStatusIcon:tooltip-markup:
407    *
408    * Sets the text of tooltip to be the given string, which is marked up
409    * with the [Pango text markup language][PangoMarkupFormat].
410    * Also see gtk_tooltip_set_markup().
411    *
412    * This is a convenience property which will take care of getting the
413    * tooltip shown if the given string is not %NULL.
414    * #GtkStatusIcon:has-tooltip will automatically be set to %TRUE and
415    * the default handler for the #GtkStatusIcon::query-tooltip signal
416    * will take care of displaying the tooltip.
417    *
418    * On some platforms, embedded markup will be ignored.
419    *
420    * Since: 2.16
421    */
422   g_object_class_install_property (gobject_class,
423 				   PROP_TOOLTIP_MARKUP,
424 				   g_param_spec_string ("tooltip-markup",
425  							P_("Tooltip markup"),
426 							P_("The contents of the tooltip for this tray icon"),
427 							NULL,
428 							GTK_PARAM_READWRITE));
429 
430 
431   /**
432    * GtkStatusIcon:title:
433    *
434    * The title of this tray icon. This should be a short, human-readable,
435    * localized string describing the tray icon. It may be used by tools
436    * like screen readers to render the tray icon.
437    *
438    * Since: 2.18
439    */
440   g_object_class_install_property (gobject_class,
441                                    PROP_TITLE,
442                                    g_param_spec_string ("title",
443                                                         P_("Title"),
444                                                         P_("The title of this tray icon"),
445                                                         NULL,
446                                                         GTK_PARAM_READWRITE));
447 
448   /**
449    * GtkStatusIcon::activate:
450    * @status_icon: the object which received the signal
451    *
452    * Gets emitted when the user activates the status icon.
453    * If and how status icons can activated is platform-dependent.
454    *
455    * Unlike most G_SIGNAL_ACTION signals, this signal is meant to
456    * be used by applications and should be wrapped by language bindings.
457    *
458    * Since: 2.10
459    */
460   status_icon_signals [ACTIVATE_SIGNAL] =
461     g_signal_new (I_("activate"),
462 		  G_TYPE_FROM_CLASS (gobject_class),
463 		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
464 		  G_STRUCT_OFFSET (GtkStatusIconClass, activate),
465 		  NULL,
466 		  NULL,
467 		  NULL,
468 		  G_TYPE_NONE,
469 		  0);
470 
471   /**
472    * GtkStatusIcon::popup-menu:
473    * @status_icon: the object which received the signal
474    * @button: the button that was pressed, or 0 if the
475    *   signal is not emitted in response to a button press event
476    * @activate_time: the timestamp of the event that
477    *   triggered the signal emission
478    *
479    * Gets emitted when the user brings up the context menu
480    * of the status icon. Whether status icons can have context
481    * menus and how these are activated is platform-dependent.
482    *
483    * The @button and @activate_time parameters should be
484    * passed as the last to arguments to gtk_menu_popup().
485    *
486    * Unlike most G_SIGNAL_ACTION signals, this signal is meant to
487    * be used by applications and should be wrapped by language bindings.
488    *
489    * Since: 2.10
490    */
491   status_icon_signals [POPUP_MENU_SIGNAL] =
492     g_signal_new (I_("popup-menu"),
493 		  G_TYPE_FROM_CLASS (gobject_class),
494 		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
495 		  G_STRUCT_OFFSET (GtkStatusIconClass, popup_menu),
496 		  NULL,
497 		  NULL,
498 		  _gtk_marshal_VOID__UINT_UINT,
499 		  G_TYPE_NONE,
500 		  2,
501 		  G_TYPE_UINT,
502 		  G_TYPE_UINT);
503 
504   /**
505    * GtkStatusIcon::size-changed:
506    * @status_icon: the object which received the signal
507    * @size: the new size
508    *
509    * Gets emitted when the size available for the image
510    * changes, e.g. because the notification area got resized.
511    *
512    * Returns: %TRUE if the icon was updated for the new
513    * size. Otherwise, GTK+ will scale the icon as necessary.
514    *
515    * Since: 2.10
516    */
517   status_icon_signals [SIZE_CHANGED_SIGNAL] =
518     g_signal_new (I_("size-changed"),
519 		  G_TYPE_FROM_CLASS (gobject_class),
520 		  G_SIGNAL_RUN_LAST,
521 		  G_STRUCT_OFFSET (GtkStatusIconClass, size_changed),
522 		  g_signal_accumulator_true_handled,
523 		  NULL,
524 		  _gtk_marshal_BOOLEAN__INT,
525 		  G_TYPE_BOOLEAN,
526 		  1,
527 		  G_TYPE_INT);
528 
529   /**
530    * GtkStatusIcon::button-press-event:
531    * @status_icon: the object which received the signal
532    * @event: (type Gdk.EventButton): the #GdkEventButton which triggered
533    *                                 this signal
534    *
535    * The ::button-press-event signal will be emitted when a button
536    * (typically from a mouse) is pressed.
537    *
538    * Whether this event is emitted is platform-dependent.  Use the ::activate
539    * and ::popup-menu signals in preference.
540    *
541    * Returns: %TRUE to stop other handlers from being invoked
542    * for the event. %FALSE to propagate the event further.
543    *
544    * Since: 2.14
545    */
546   status_icon_signals [BUTTON_PRESS_EVENT_SIGNAL] =
547     g_signal_new (I_("button_press_event"),
548 		  G_TYPE_FROM_CLASS (gobject_class),
549 		  G_SIGNAL_RUN_LAST,
550 		  G_STRUCT_OFFSET (GtkStatusIconClass, button_press_event),
551 		  g_signal_accumulator_true_handled, NULL,
552 		  _gtk_marshal_BOOLEAN__BOXED,
553 		  G_TYPE_BOOLEAN, 1,
554 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
555 
556   /**
557    * GtkStatusIcon::button-release-event:
558    * @status_icon: the object which received the signal
559    * @event: (type Gdk.EventButton): the #GdkEventButton which triggered
560    *                                 this signal
561    *
562    * The ::button-release-event signal will be emitted when a button
563    * (typically from a mouse) is released.
564    *
565    * Whether this event is emitted is platform-dependent.  Use the ::activate
566    * and ::popup-menu signals in preference.
567    *
568    * Returns: %TRUE to stop other handlers from being invoked
569    * for the event. %FALSE to propagate the event further.
570    *
571    * Since: 2.14
572    */
573   status_icon_signals [BUTTON_RELEASE_EVENT_SIGNAL] =
574     g_signal_new (I_("button_release_event"),
575 		  G_TYPE_FROM_CLASS (gobject_class),
576 		  G_SIGNAL_RUN_LAST,
577 		  G_STRUCT_OFFSET (GtkStatusIconClass, button_release_event),
578 		  g_signal_accumulator_true_handled, NULL,
579 		  _gtk_marshal_BOOLEAN__BOXED,
580 		  G_TYPE_BOOLEAN, 1,
581 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
582 
583   /**
584    * GtkStatusIcon::scroll-event:
585    * @status_icon: the object which received the signal.
586    * @event: (type Gdk.EventScroll): the #GdkEventScroll which triggered
587    *                                 this signal
588    *
589    * The ::scroll-event signal is emitted when a button in the 4 to 7
590    * range is pressed. Wheel mice are usually configured to generate
591    * button press events for buttons 4 and 5 when the wheel is turned.
592    *
593    * Whether this event is emitted is platform-dependent.
594    *
595    * Returns: %TRUE to stop other handlers from being invoked for the event.
596    *   %FALSE to propagate the event further.
597    *
598    * Since: 2.16
599    */
600   status_icon_signals[SCROLL_EVENT_SIGNAL] =
601     g_signal_new (I_("scroll_event"),
602 		  G_TYPE_FROM_CLASS (gobject_class),
603 		  G_SIGNAL_RUN_LAST,
604 		  G_STRUCT_OFFSET (GtkStatusIconClass, scroll_event),
605 		  g_signal_accumulator_true_handled, NULL,
606 		  _gtk_marshal_BOOLEAN__BOXED,
607 		  G_TYPE_BOOLEAN, 1,
608 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
609 
610   /**
611    * GtkStatusIcon::query-tooltip:
612    * @status_icon: the object which received the signal
613    * @x: the x coordinate of the cursor position where the request has been
614    *     emitted, relative to @status_icon
615    * @y: the y coordinate of the cursor position where the request has been
616    *     emitted, relative to @status_icon
617    * @keyboard_mode: %TRUE if the tooltip was trigged using the keyboard
618    * @tooltip: a #GtkTooltip
619    *
620    * Emitted when the hover timeout has expired with the
621    * cursor hovering above @status_icon; or emitted when @status_icon got
622    * focus in keyboard mode.
623    *
624    * Using the given coordinates, the signal handler should determine
625    * whether a tooltip should be shown for @status_icon. If this is
626    * the case %TRUE should be returned, %FALSE otherwise. Note that if
627    * @keyboard_mode is %TRUE, the values of @x and @y are undefined and
628    * should not be used.
629    *
630    * The signal handler is free to manipulate @tooltip with the therefore
631    * destined function calls.
632    *
633    * Whether this signal is emitted is platform-dependent.
634    * For plain text tooltips, use #GtkStatusIcon:tooltip-text in preference.
635    *
636    * Returns: %TRUE if @tooltip should be shown right now, %FALSE otherwise.
637    *
638    * Since: 2.16
639    */
640   status_icon_signals [QUERY_TOOLTIP_SIGNAL] =
641     g_signal_new (I_("query_tooltip"),
642 		  G_TYPE_FROM_CLASS (gobject_class),
643 		  G_SIGNAL_RUN_LAST,
644 		  G_STRUCT_OFFSET (GtkStatusIconClass, query_tooltip),
645 		  g_signal_accumulator_true_handled, NULL,
646 		  _gtk_marshal_BOOLEAN__INT_INT_BOOLEAN_OBJECT,
647 		  G_TYPE_BOOLEAN, 4,
648 		  G_TYPE_INT,
649 		  G_TYPE_INT,
650 		  G_TYPE_BOOLEAN,
651 		  GTK_TYPE_TOOLTIP);
652 }
653 
654 #ifdef GDK_WINDOWING_WIN32
655 
656 static void
build_button_event(GtkStatusIconPrivate * priv,GdkEventButton * e,guint button)657 build_button_event (GtkStatusIconPrivate *priv,
658 		    GdkEventButton       *e,
659 		    guint                 button)
660 {
661   POINT pos;
662   GdkRectangle monitor0;
663 
664   /* We know that gdk/win32 puts the primary monitor at index 0 */
665   gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor0);
666   e->window = g_object_ref (gdk_get_default_root_window ());
667   e->send_event = TRUE;
668   e->time = GetTickCount ();
669   GetCursorPos (&pos);
670   priv->last_click_x = e->x = pos.x + monitor0.x;
671   priv->last_click_y = e->y = pos.y + monitor0.y;
672   e->axes = NULL;
673   e->state = 0;
674   e->button = button;
675   //FIXME: e->device = gdk_display_get_default ()->core_pointer;
676   e->x_root = e->x;
677   e->y_root = e->y;
678 }
679 
680 typedef struct
681 {
682   GtkStatusIcon *status_icon;
683   GdkEventButton *event;
684 } ButtonCallbackData;
685 
686 static gboolean
button_callback(gpointer data)687 button_callback (gpointer data)
688 {
689   ButtonCallbackData *bc = (ButtonCallbackData *) data;
690 
691   if (bc->event->type == GDK_BUTTON_PRESS)
692     gtk_status_icon_button_press (bc->status_icon, bc->event);
693   else
694     gtk_status_icon_button_release (bc->status_icon, bc->event);
695 
696   gdk_event_free ((GdkEvent *) bc->event);
697   g_free (data);
698 
699   return G_SOURCE_REMOVE;
700 }
701 
702 static UINT taskbar_created_msg = 0;
703 static GSList *status_icons = NULL;
704 static UINT status_icon_id = 0;
705 
706 static GtkStatusIcon *
find_status_icon(UINT id)707 find_status_icon (UINT id)
708 {
709   GSList *rover;
710 
711   for (rover = status_icons; rover != NULL; rover = rover->next)
712     {
713       GtkStatusIcon *status_icon = GTK_STATUS_ICON (rover->data);
714       GtkStatusIconPrivate *priv = status_icon->priv;
715 
716       if (priv->nid.uID == id)
717         return status_icon;
718     }
719 
720   return NULL;
721 }
722 
723 static LRESULT CALLBACK
wndproc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)724 wndproc (HWND   hwnd,
725 	 UINT   message,
726 	 WPARAM wparam,
727 	 LPARAM lparam)
728 {
729   if (message == taskbar_created_msg)
730     {
731       GSList *rover;
732 
733       for (rover = status_icons; rover != NULL; rover = rover->next)
734 	{
735 	  GtkStatusIcon *status_icon = GTK_STATUS_ICON (rover->data);
736 	  GtkStatusIconPrivate *priv = status_icon->priv;
737 
738           if (priv->visible)
739             {
740               /* taskbar_created_msg is also fired when DPI changes. Try to delete existing icons if possible. */
741               if (priv->nid.hWnd)
742                 if (!Shell_NotifyIconW (NIM_DELETE, &priv->nid))
743                   g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_DELETE) on existing icon failed");
744 
745               priv->nid.hWnd = hwnd;
746               priv->nid.uFlags &= ~NIF_ICON;
747 
748               if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
749                 {
750                   g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed");
751                   priv->nid.hWnd = NULL;
752                   continue;
753                 }
754 
755               gtk_status_icon_update_image (status_icon);
756             }
757 	}
758       return 0;
759     }
760 
761   if (message == WM_GTK_TRAY_NOTIFICATION)
762     {
763       ButtonCallbackData *bc;
764       guint button;
765 
766       switch (lparam)
767 	{
768 	case WM_LBUTTONDOWN:
769 	  button = 1;
770 	  goto buttondown0;
771 
772 	case WM_MBUTTONDOWN:
773 	  button = 2;
774 	  goto buttondown0;
775 
776 	case WM_RBUTTONDOWN:
777 	  button = 3;
778 	  goto buttondown0;
779 
780 	case WM_XBUTTONDOWN:
781 	  if (HIWORD (wparam) == XBUTTON1)
782 	    button = 4;
783 	  else
784 	    button = 5;
785 
786 	buttondown0:
787 	  bc = g_new (ButtonCallbackData, 1);
788 	  bc->event = (GdkEventButton *) gdk_event_new (GDK_BUTTON_PRESS);
789 	  bc->status_icon = find_status_icon (wparam);
790 	  build_button_event (bc->status_icon->priv, bc->event, button);
791 	  g_idle_add (button_callback, bc);
792 	  break;
793 
794 	case WM_LBUTTONUP:
795 	  button = 1;
796 	  goto buttonup0;
797 
798 	case WM_MBUTTONUP:
799 	  button = 2;
800 	  goto buttonup0;
801 
802 	case WM_RBUTTONUP:
803 	  button = 3;
804 	  goto buttonup0;
805 
806 	case WM_XBUTTONUP:
807 	  if (HIWORD (wparam) == XBUTTON1)
808 	    button = 4;
809 	  else
810 	    button = 5;
811 
812 	buttonup0:
813 	  bc = g_new (ButtonCallbackData, 1);
814 	  bc->event = (GdkEventButton *) gdk_event_new (GDK_BUTTON_RELEASE);
815 	  bc->status_icon = find_status_icon (wparam);
816 	  build_button_event (bc->status_icon->priv, bc->event, button);
817 	  g_idle_add (button_callback, bc);
818 	  break;
819 
820 	default :
821 	  break;
822 	}
823 	return 0;
824     }
825   else
826     {
827       return DefWindowProc (hwnd, message, wparam, lparam);
828     }
829 }
830 
831 static HWND
create_tray_observer(void)832 create_tray_observer (void)
833 {
834   WNDCLASS    wclass;
835   static HWND hwnd = NULL;
836   ATOM        klass;
837   HINSTANCE   hmodule = GetModuleHandle (NULL);
838 
839   if (hwnd)
840     return hwnd;
841 
842   taskbar_created_msg = RegisterWindowMessage("TaskbarCreated");
843 
844   memset (&wclass, 0, sizeof(WNDCLASS));
845   wclass.lpszClassName = "gtkstatusicon-observer";
846   wclass.lpfnWndProc   = wndproc;
847   wclass.hInstance     = hmodule;
848 
849   klass = RegisterClass (&wclass);
850   if (!klass)
851     return NULL;
852 
853   hwnd = CreateWindow (MAKEINTRESOURCE (klass),
854                        NULL, WS_POPUP,
855                        0, 0, 1, 1, NULL, NULL,
856                        hmodule, NULL);
857   if (!hwnd)
858     {
859       UnregisterClass (MAKEINTRESOURCE(klass), hmodule);
860       return NULL;
861     }
862 
863   return hwnd;
864 }
865 
866 #endif
867 
868 static void
gtk_status_icon_init(GtkStatusIcon * status_icon)869 gtk_status_icon_init (GtkStatusIcon *status_icon)
870 {
871   GtkStatusIconPrivate *priv;
872 
873   priv = gtk_status_icon_get_instance_private (status_icon);
874   status_icon->priv = priv;
875 
876   priv->image_def = gtk_image_definition_new_empty ();
877   priv->visible = TRUE;
878 
879 #ifdef GDK_WINDOWING_X11
880   if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
881     {
882       priv->size         = 0;
883       priv->tray_icon = GTK_WIDGET (_gtk_tray_icon_new (NULL));
884 
885       gtk_widget_add_events (GTK_WIDGET (priv->tray_icon),
886                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
887                              GDK_SCROLL_MASK);
888 
889       g_signal_connect_swapped (priv->tray_icon, "key-press-event",
890                                 G_CALLBACK (gtk_status_icon_key_press), status_icon);
891       g_signal_connect_swapped (priv->tray_icon, "popup-menu",
892                                 G_CALLBACK (gtk_status_icon_popup_menu), status_icon);
893       g_signal_connect_swapped (priv->tray_icon, "notify::embedded",
894                                 G_CALLBACK (gtk_status_icon_embedded_changed), status_icon);
895       g_signal_connect_swapped (priv->tray_icon, "notify::orientation",
896                                 G_CALLBACK (gtk_status_icon_orientation_changed), status_icon);
897       g_signal_connect_swapped (priv->tray_icon, "notify::padding",
898                                 G_CALLBACK (gtk_status_icon_padding_changed), status_icon);
899       g_signal_connect_swapped (priv->tray_icon, "notify::icon-size",
900                                 G_CALLBACK (gtk_status_icon_icon_size_changed), status_icon);
901       g_signal_connect_swapped (priv->tray_icon, "notify::fg-color",
902                                 G_CALLBACK (gtk_status_icon_fg_changed), status_icon);
903       g_signal_connect (priv->tray_icon, "notify::error-color",
904                         G_CALLBACK (gtk_status_icon_color_changed), status_icon);
905       g_signal_connect (priv->tray_icon, "notify::warning-color",
906                         G_CALLBACK (gtk_status_icon_color_changed), status_icon);
907       g_signal_connect (priv->tray_icon, "notify::success-color",
908                         G_CALLBACK (gtk_status_icon_color_changed), status_icon);
909       g_signal_connect_swapped (priv->tray_icon, "button-press-event",
910                                 G_CALLBACK (gtk_status_icon_button_press), status_icon);
911       g_signal_connect_swapped (priv->tray_icon, "button-release-event",
912                                 G_CALLBACK (gtk_status_icon_button_release), status_icon);
913       g_signal_connect_swapped (priv->tray_icon, "scroll-event",
914                                 G_CALLBACK (gtk_status_icon_scroll), status_icon);
915       g_signal_connect_swapped (priv->tray_icon, "query-tooltip",
916                                 G_CALLBACK (gtk_status_icon_query_tooltip), status_icon);
917       g_signal_connect_swapped (priv->tray_icon, "screen-changed",
918                                 G_CALLBACK (gtk_status_icon_screen_changed), status_icon);
919       priv->image = gtk_image_new ();
920       gtk_widget_set_can_focus (priv->image, TRUE);
921       gtk_container_add (GTK_CONTAINER (priv->tray_icon), priv->image);
922       gtk_widget_show (priv->image);
923 
924       /* Force-initialize the symbolic colors */
925       g_object_notify (G_OBJECT (priv->tray_icon), "fg-color");
926       g_object_notify (G_OBJECT (priv->tray_icon), "error-color");
927       g_object_notify (G_OBJECT (priv->tray_icon), "warning-color");
928       g_object_notify (G_OBJECT (priv->tray_icon), "success-color");
929 
930       g_signal_connect_swapped (priv->image, "size-allocate",
931                                 G_CALLBACK (gtk_status_icon_size_allocate), status_icon);
932     }
933 #else /* !GDK_WINDOWING_X11 */
934   priv->dummy_widget = gtk_label_new ("");
935 #endif
936 
937 #ifdef GDK_WINDOWING_WIN32
938 
939   /* Get position and orientation of Windows taskbar. */
940   {
941     APPBARDATA abd;
942 
943     abd.cbSize = sizeof (abd);
944     SHAppBarMessage (ABM_GETTASKBARPOS, &abd);
945     if (abd.rc.bottom - abd.rc.top > abd.rc.right - abd.rc.left)
946       priv->orientation = GTK_ORIENTATION_VERTICAL;
947     else
948       priv->orientation = GTK_ORIENTATION_HORIZONTAL;
949 
950     priv->taskbar_top = abd.rc.top;
951   }
952 
953   priv->last_click_x = priv->last_click_y = 0;
954 
955   /* Are the system tray icons always 16 pixels square? */
956   priv->size         = 16;
957 
958   memset (&priv->nid, 0, sizeof (priv->nid));
959 
960   priv->nid.hWnd = create_tray_observer ();
961   priv->nid.uID = status_icon_id++;
962   priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
963   priv->nid.uFlags = NIF_MESSAGE;
964 
965   /* To help win7 identify the icon create it with an application "unique" tip */
966   if (g_get_prgname ())
967   {
968     WCHAR *wcs = g_utf8_to_utf16 (g_get_prgname (), -1, NULL, NULL, NULL);
969 
970     priv->nid.uFlags |= NIF_TIP;
971     wcsncpy (priv->nid.szTip, wcs, G_N_ELEMENTS (priv->nid.szTip) - 1);
972     priv->nid.szTip[G_N_ELEMENTS (priv->nid.szTip) - 1] = 0;
973     g_free (wcs);
974   }
975 
976   if (!Shell_NotifyIconW (NIM_ADD, &priv->nid))
977     {
978       g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed");
979       priv->nid.hWnd = NULL;
980     }
981 
982   status_icons = g_slist_append (status_icons, status_icon);
983 
984 #endif
985 
986 #ifdef GDK_WINDOWING_QUARTZ
987   QUARTZ_POOL_ALLOC;
988 
989   priv->status_item = [[GtkQuartzStatusIcon alloc] initWithStatusIcon:status_icon];
990   priv->size = [priv->status_item getHeight];
991 
992   QUARTZ_POOL_RELEASE;
993 
994 #endif
995 }
996 
997 static void
gtk_status_icon_constructed(GObject * object)998 gtk_status_icon_constructed (GObject *object)
999 {
1000   G_OBJECT_CLASS (gtk_status_icon_parent_class)->constructed (object);
1001 
1002 #ifdef GDK_WINDOWING_X11
1003   {
1004     GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
1005     GtkStatusIconPrivate *priv = status_icon->priv;
1006 
1007     if (priv->visible && priv->tray_icon)
1008       gtk_widget_show (priv->tray_icon);
1009   }
1010 #endif
1011 }
1012 
1013 static void
gtk_status_icon_finalize(GObject * object)1014 gtk_status_icon_finalize (GObject *object)
1015 {
1016   GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
1017   GtkStatusIconPrivate *priv = status_icon->priv;
1018 
1019   gtk_status_icon_reset_image_data (status_icon);
1020   gtk_image_definition_unref (priv->image_def);
1021 
1022 #ifdef GDK_WINDOWING_X11
1023   if (priv->tray_icon)
1024     {
1025       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1026                                             gtk_status_icon_key_press, status_icon);
1027       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1028                                             gtk_status_icon_popup_menu, status_icon);
1029       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1030                                             gtk_status_icon_embedded_changed, status_icon);
1031       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1032                                             gtk_status_icon_orientation_changed, status_icon);
1033       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1034                                             gtk_status_icon_padding_changed, status_icon);
1035       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1036                                             gtk_status_icon_icon_size_changed, status_icon);
1037       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1038                                             gtk_status_icon_fg_changed, status_icon);
1039       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1040                                             gtk_status_icon_color_changed, status_icon);
1041       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1042                                             gtk_status_icon_button_press, status_icon);
1043       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1044                                             gtk_status_icon_button_release, status_icon);
1045       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1046                                             gtk_status_icon_scroll, status_icon);
1047       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1048                                             gtk_status_icon_query_tooltip, status_icon);
1049       g_signal_handlers_disconnect_by_func (priv->tray_icon,
1050                                             gtk_status_icon_screen_changed, status_icon);
1051       gtk_widget_destroy (priv->image);
1052       gtk_widget_destroy (priv->tray_icon);
1053     }
1054 #else /* !GDK_WINDOWING_X11 */
1055   gtk_widget_destroy (priv->dummy_widget);
1056 #endif
1057 
1058 #ifdef GDK_WINDOWING_WIN32
1059   if (priv->nid.hWnd != NULL && priv->visible)
1060     Shell_NotifyIconW (NIM_DELETE, &priv->nid);
1061   if (priv->nid.hIcon)
1062     DestroyIcon (priv->nid.hIcon);
1063   g_free (priv->tooltip_text);
1064 
1065   status_icons = g_slist_remove (status_icons, status_icon);
1066 #endif
1067 
1068 #ifdef GDK_WINDOWING_QUARTZ
1069   QUARTZ_POOL_ALLOC;
1070   [priv->status_item release];
1071   QUARTZ_POOL_RELEASE;
1072   g_free (priv->tooltip_text);
1073 #endif
1074 
1075   G_OBJECT_CLASS (gtk_status_icon_parent_class)->finalize (object);
1076 }
1077 
1078 static void
gtk_status_icon_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1079 gtk_status_icon_set_property (GObject      *object,
1080 			      guint         prop_id,
1081 			      const GValue *value,
1082 			      GParamSpec   *pspec)
1083 {
1084   GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
1085 
1086   switch (prop_id)
1087     {
1088     case PROP_PIXBUF:
1089       gtk_status_icon_set_from_pixbuf (status_icon, g_value_get_object (value));
1090       break;
1091     case PROP_FILE:
1092       gtk_status_icon_set_from_file (status_icon, g_value_get_string (value));
1093       break;
1094     case PROP_STOCK:
1095       gtk_status_icon_set_from_stock (status_icon, g_value_get_string (value));
1096       break;
1097     case PROP_ICON_NAME:
1098       gtk_status_icon_set_from_icon_name (status_icon, g_value_get_string (value));
1099       break;
1100     case PROP_GICON:
1101       gtk_status_icon_set_from_gicon (status_icon, g_value_get_object (value));
1102       break;
1103     case PROP_SCREEN:
1104       gtk_status_icon_set_screen (status_icon, g_value_get_object (value));
1105       break;
1106     case PROP_VISIBLE:
1107       gtk_status_icon_set_visible (status_icon, g_value_get_boolean (value));
1108       break;
1109     case PROP_HAS_TOOLTIP:
1110       gtk_status_icon_set_has_tooltip (status_icon, g_value_get_boolean (value));
1111       break;
1112     case PROP_TOOLTIP_TEXT:
1113       gtk_status_icon_set_tooltip_text (status_icon, g_value_get_string (value));
1114       break;
1115     case PROP_TOOLTIP_MARKUP:
1116       gtk_status_icon_set_tooltip_markup (status_icon, g_value_get_string (value));
1117       break;
1118     case PROP_TITLE:
1119       gtk_status_icon_set_title (status_icon, g_value_get_string (value));
1120       break;
1121     default:
1122       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1123       break;
1124     }
1125 }
1126 
1127 static void
gtk_status_icon_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1128 gtk_status_icon_get_property (GObject    *object,
1129 			      guint       prop_id,
1130 			      GValue     *value,
1131 			      GParamSpec *pspec)
1132 {
1133   GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
1134 
1135   switch (prop_id)
1136     {
1137     case PROP_PIXBUF:
1138       g_value_set_object (value, gtk_status_icon_get_pixbuf (status_icon));
1139       break;
1140     case PROP_STOCK:
1141       g_value_set_string (value, gtk_status_icon_get_stock (status_icon));
1142       break;
1143     case PROP_ICON_NAME:
1144       g_value_set_string (value, gtk_status_icon_get_icon_name (status_icon));
1145       break;
1146     case PROP_GICON:
1147       g_value_set_object (value, gtk_status_icon_get_gicon (status_icon));
1148       break;
1149     case PROP_STORAGE_TYPE:
1150       g_value_set_enum (value, gtk_status_icon_get_storage_type (status_icon));
1151       break;
1152     case PROP_SIZE:
1153       g_value_set_int (value, gtk_status_icon_get_size (status_icon));
1154       break;
1155     case PROP_SCREEN:
1156       g_value_set_object (value, gtk_status_icon_get_screen (status_icon));
1157       break;
1158     case PROP_VISIBLE:
1159       g_value_set_boolean (value, gtk_status_icon_get_visible (status_icon));
1160       break;
1161     case PROP_EMBEDDED:
1162       g_value_set_boolean (value, gtk_status_icon_is_embedded (status_icon));
1163       break;
1164     case PROP_ORIENTATION:
1165 #ifdef GDK_WINDOWING_X11
1166       if (status_icon->priv->tray_icon)
1167         g_value_set_enum (value, _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (status_icon->priv->tray_icon)));
1168       else
1169         g_value_set_enum (value, GTK_ORIENTATION_HORIZONTAL);
1170 #endif
1171 #ifdef GDK_WINDOWING_WIN32
1172       g_value_set_enum (value, status_icon->priv->orientation);
1173 #endif
1174       break;
1175     case PROP_HAS_TOOLTIP:
1176       g_value_set_boolean (value, gtk_status_icon_get_has_tooltip (status_icon));
1177       break;
1178     case PROP_TOOLTIP_TEXT:
1179       g_value_set_string (value, gtk_status_icon_get_tooltip_text (status_icon));
1180       break;
1181     case PROP_TOOLTIP_MARKUP:
1182       g_value_set_string (value, gtk_status_icon_get_tooltip_markup (status_icon));
1183       break;
1184     case PROP_TITLE:
1185       g_value_set_string (value, gtk_status_icon_get_title (status_icon));
1186       break;
1187     default:
1188       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1189       break;
1190     }
1191 }
1192 
1193 /**
1194  * gtk_status_icon_new:
1195  *
1196  * Creates an empty status icon object.
1197  *
1198  * Returns: a new #GtkStatusIcon
1199  *
1200  * Since: 2.10
1201  *
1202  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1203  *   provide status notifications
1204  */
1205 GtkStatusIcon *
gtk_status_icon_new(void)1206 gtk_status_icon_new (void)
1207 {
1208   return g_object_new (GTK_TYPE_STATUS_ICON, NULL);
1209 }
1210 
1211 /**
1212  * gtk_status_icon_new_from_pixbuf:
1213  * @pixbuf: a #GdkPixbuf
1214  *
1215  * Creates a status icon displaying @pixbuf.
1216  *
1217  * The image will be scaled down to fit in the available
1218  * space in the notification area, if necessary.
1219  *
1220  * Returns: a new #GtkStatusIcon
1221  *
1222  * Since: 2.10
1223  *
1224  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1225  *   provide status notifications
1226  */
1227 GtkStatusIcon *
gtk_status_icon_new_from_pixbuf(GdkPixbuf * pixbuf)1228 gtk_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf)
1229 {
1230   return g_object_new (GTK_TYPE_STATUS_ICON,
1231 		       "pixbuf", pixbuf,
1232 		       NULL);
1233 }
1234 
1235 /**
1236  * gtk_status_icon_new_from_file:
1237  * @filename: (type filename): a filename
1238  *
1239  * Creates a status icon displaying the file @filename.
1240  *
1241  * The image will be scaled down to fit in the available
1242  * space in the notification area, if necessary.
1243  *
1244  * Returns: a new #GtkStatusIcon
1245  *
1246  * Since: 2.10
1247  *
1248  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1249  *   provide status notifications
1250  */
1251 GtkStatusIcon *
gtk_status_icon_new_from_file(const gchar * filename)1252 gtk_status_icon_new_from_file (const gchar *filename)
1253 {
1254   return g_object_new (GTK_TYPE_STATUS_ICON,
1255 		       "file", filename,
1256 		       NULL);
1257 }
1258 
1259 /**
1260  * gtk_status_icon_new_from_stock:
1261  * @stock_id: a stock icon id
1262  *
1263  * Creates a status icon displaying a stock icon. Sample stock icon
1264  * names are #GTK_STOCK_OPEN, #GTK_STOCK_QUIT. You can register your
1265  * own stock icon names, see gtk_icon_factory_add_default() and
1266  * gtk_icon_factory_add().
1267  *
1268  * Returns: a new #GtkStatusIcon
1269  *
1270  * Since: 2.10
1271  *
1272  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1273  *   provide status notifications
1274  */
1275 GtkStatusIcon *
gtk_status_icon_new_from_stock(const gchar * stock_id)1276 gtk_status_icon_new_from_stock (const gchar *stock_id)
1277 {
1278   return g_object_new (GTK_TYPE_STATUS_ICON,
1279 		       "stock", stock_id,
1280 		       NULL);
1281 }
1282 
1283 /**
1284  * gtk_status_icon_new_from_icon_name:
1285  * @icon_name: an icon name
1286  *
1287  * Creates a status icon displaying an icon from the current icon theme.
1288  * If the current icon theme is changed, the icon will be updated
1289  * appropriately.
1290  *
1291  * Returns: a new #GtkStatusIcon
1292  *
1293  * Since: 2.10
1294  *
1295  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1296  *   provide status notifications
1297  */
1298 GtkStatusIcon *
gtk_status_icon_new_from_icon_name(const gchar * icon_name)1299 gtk_status_icon_new_from_icon_name (const gchar *icon_name)
1300 {
1301   return g_object_new (GTK_TYPE_STATUS_ICON,
1302 		       "icon-name", icon_name,
1303 		       NULL);
1304 }
1305 
1306 /**
1307  * gtk_status_icon_new_from_gicon:
1308  * @icon: a #GIcon
1309  *
1310  * Creates a status icon displaying a #GIcon. If the icon is a
1311  * themed icon, it will be updated when the theme changes.
1312  *
1313  * Returns: a new #GtkStatusIcon
1314  *
1315  * Since: 2.14
1316  *
1317  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1318  *   provide status notifications
1319  */
1320 GtkStatusIcon *
gtk_status_icon_new_from_gicon(GIcon * icon)1321 gtk_status_icon_new_from_gicon (GIcon *icon)
1322 {
1323   return g_object_new (GTK_TYPE_STATUS_ICON,
1324 		       "gicon", icon,
1325 		       NULL);
1326 }
1327 
1328 static void
emit_activate_signal(GtkStatusIcon * status_icon)1329 emit_activate_signal (GtkStatusIcon *status_icon)
1330 {
1331   g_signal_emit (status_icon,
1332 		 status_icon_signals [ACTIVATE_SIGNAL], 0);
1333 }
1334 
1335 static void
emit_popup_menu_signal(GtkStatusIcon * status_icon,guint button,guint32 activate_time)1336 emit_popup_menu_signal (GtkStatusIcon *status_icon,
1337 			guint          button,
1338 			guint32        activate_time)
1339 {
1340   g_signal_emit (status_icon,
1341 		 status_icon_signals [POPUP_MENU_SIGNAL], 0,
1342 		 button,
1343 		 activate_time);
1344 }
1345 
1346 #ifdef GDK_WINDOWING_X11
1347 
1348 static gboolean
emit_size_changed_signal(GtkStatusIcon * status_icon,gint size)1349 emit_size_changed_signal (GtkStatusIcon *status_icon,
1350 			  gint           size)
1351 {
1352   gboolean handled = FALSE;
1353 
1354   g_signal_emit (status_icon,
1355 		 status_icon_signals [SIZE_CHANGED_SIGNAL], 0,
1356 		 size,
1357 		 &handled);
1358 
1359   return handled;
1360 }
1361 
1362 #endif
1363 
1364 /* rounds the pixel size to the nearest size avaiable in the theme */
1365 static gint
round_pixel_size(GtkWidget * widget,gint pixel_size)1366 round_pixel_size (GtkWidget *widget,
1367                   gint       pixel_size)
1368 {
1369   GtkIconSize s;
1370   gint w, h, d, dist, size;
1371 
1372   dist = G_MAXINT;
1373   size = 0;
1374 
1375   for (s = GTK_ICON_SIZE_MENU; s <= GTK_ICON_SIZE_DIALOG; s++)
1376     {
1377       if (gtk_icon_size_lookup (s, &w, &h))
1378 	{
1379 	  d = MAX (abs (pixel_size - w), abs (pixel_size - h));
1380 	  if (d < dist)
1381 	    {
1382 	      dist = d;
1383               size = MAX (w, h);
1384 	    }
1385 	}
1386     }
1387 
1388   return size;
1389 }
1390 
1391 static void
gtk_status_icon_update_image(GtkStatusIcon * status_icon)1392 gtk_status_icon_update_image (GtkStatusIcon *status_icon)
1393 {
1394   GtkStatusIconPrivate *priv = status_icon->priv;
1395 #ifdef GDK_WINDOWING_WIN32
1396   HICON prev_hicon;
1397 #endif
1398   GtkIconHelper *icon_helper;
1399   cairo_surface_t *surface;
1400   GtkWidget *widget;
1401 #ifndef GDK_WINDOWING_X11
1402   GdkPixbuf *pixbuf;
1403 #endif
1404   gint round_size;
1405   gint scale;
1406 
1407 #ifdef GDK_WINDOWING_X11
1408   widget = priv->image;
1409   scale = gtk_widget_get_scale_factor (widget);
1410 #else
1411   widget = priv->dummy_widget;
1412   scale = 1;
1413 #endif
1414 
1415   if (widget == NULL)
1416     return;
1417 
1418   round_size = round_pixel_size (widget, priv->size);
1419 
1420   icon_helper = gtk_icon_helper_new (gtk_style_context_get_node (gtk_widget_get_style_context (widget)), widget);
1421   _gtk_icon_helper_set_force_scale_pixbuf (icon_helper, TRUE);
1422   _gtk_icon_helper_set_definition (icon_helper, priv->image_def);
1423   _gtk_icon_helper_set_icon_size (icon_helper, GTK_ICON_SIZE_SMALL_TOOLBAR);
1424   _gtk_icon_helper_set_pixel_size (icon_helper, round_size);
1425   surface = gtk_icon_helper_load_surface (icon_helper, scale);
1426 
1427   g_object_unref (icon_helper);
1428 
1429 #ifdef GDK_WINDOWING_X11
1430   if (surface)
1431     {
1432       gtk_image_set_from_surface (GTK_IMAGE (priv->image), surface);
1433       cairo_surface_destroy (surface);
1434     }
1435   else
1436     {
1437       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
1438     }
1439 #endif
1440 
1441 #ifdef GDK_WINDOWING_WIN32
1442   if (surface)
1443     {
1444       pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0,
1445                                             cairo_image_surface_get_width (surface),
1446                                             cairo_image_surface_get_height (surface));
1447       cairo_surface_destroy (surface);
1448     }
1449 
1450   if (pixbuf != NULL)
1451     {
1452       prev_hicon = priv->nid.hIcon;
1453       priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (pixbuf);
1454       priv->nid.uFlags |= NIF_ICON;
1455       if (priv->nid.hWnd != NULL && priv->visible)
1456         if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
1457           g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed");
1458       if (prev_hicon)
1459         DestroyIcon (prev_hicon);
1460     }
1461   else
1462     {
1463       priv->nid.uFlags &= ~NIF_ICON;
1464       if (priv->nid.hWnd != NULL && priv->visible)
1465         if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
1466           g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed");
1467     }
1468 
1469   g_clear_object (&pixbuf);
1470 #endif
1471 
1472 #ifdef GDK_WINDOWING_QUARTZ
1473   if (surface)
1474     {
1475       pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0,
1476                                             cairo_image_surface_get_width (surface),
1477                                             cairo_image_surface_get_height (surface));
1478       cairo_surface_destroy (surface);
1479     }
1480 
1481   if (pixbuf != NULL)
1482     {
1483       QUARTZ_POOL_ALLOC;
1484       [priv->status_item setImage:pixbuf];
1485       QUARTZ_POOL_RELEASE;
1486     }
1487   else
1488     {
1489       [priv->status_item setImage:NULL];
1490     }
1491 
1492   g_clear_object (&pixbuf);
1493 #endif
1494 }
1495 
1496 #ifdef GDK_WINDOWING_X11
1497 
1498 static void
gtk_status_icon_size_allocate(GtkStatusIcon * status_icon,GtkAllocation * allocation)1499 gtk_status_icon_size_allocate (GtkStatusIcon *status_icon,
1500 			       GtkAllocation *allocation)
1501 {
1502   GtkStatusIconPrivate *priv = status_icon->priv;
1503   GtkOrientation orientation;
1504   gint size;
1505 
1506   orientation = _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (priv->tray_icon));
1507 
1508   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1509     size = allocation->height;
1510   else
1511     size = allocation->width;
1512 
1513   if (priv->size - 1 > size || priv->size + 1 < size)
1514     {
1515       priv->size = size;
1516 
1517       g_object_notify (G_OBJECT (status_icon), "size");
1518 
1519       if (!emit_size_changed_signal (status_icon, size))
1520 	gtk_status_icon_update_image (status_icon);
1521     }
1522 }
1523 
1524 static void
gtk_status_icon_screen_changed(GtkStatusIcon * status_icon,GdkScreen * old_screen)1525 gtk_status_icon_screen_changed (GtkStatusIcon *status_icon,
1526 				GdkScreen *old_screen)
1527 {
1528   GtkStatusIconPrivate *priv = status_icon->priv;
1529 
1530   if (gtk_widget_get_screen (priv->tray_icon) != old_screen)
1531     {
1532       g_object_notify (G_OBJECT (status_icon), "screen");
1533     }
1534 }
1535 
1536 static void
gtk_status_icon_padding_changed(GtkStatusIcon * status_icon)1537 gtk_status_icon_padding_changed (GtkStatusIcon *status_icon)
1538 {
1539   GtkStatusIconPrivate *priv = status_icon->priv;
1540   GtkOrientation orientation;
1541   gint padding;
1542 
1543   orientation = _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (priv->tray_icon));
1544   padding = _gtk_tray_icon_get_padding (GTK_TRAY_ICON (priv->tray_icon));
1545 
1546   if (orientation == GTK_ORIENTATION_HORIZONTAL)
1547     {
1548       gtk_widget_set_margin_start (priv->image, padding);
1549       gtk_widget_set_margin_end (priv->image, padding);
1550     }
1551   else
1552     {
1553       gtk_widget_set_margin_bottom (priv->image, padding);
1554       gtk_widget_set_margin_top (priv->image, padding);
1555     }
1556 }
1557 
1558 static void
gtk_status_icon_icon_size_changed(GtkStatusIcon * status_icon)1559 gtk_status_icon_icon_size_changed (GtkStatusIcon *status_icon)
1560 {
1561   GtkStatusIconPrivate *priv = status_icon->priv;
1562   gint icon_size;
1563 
1564   icon_size = _gtk_tray_icon_get_icon_size (GTK_TRAY_ICON (priv->tray_icon));
1565 
1566   if (icon_size != 0)
1567     gtk_image_set_pixel_size (GTK_IMAGE (priv->image), icon_size);
1568   else
1569     gtk_image_set_pixel_size (GTK_IMAGE (priv->image), -1);
1570 }
1571 
1572 static void
gtk_status_icon_embedded_changed(GtkStatusIcon * status_icon)1573 gtk_status_icon_embedded_changed (GtkStatusIcon *status_icon)
1574 {
1575   gtk_status_icon_padding_changed (status_icon);
1576   gtk_status_icon_icon_size_changed (status_icon);
1577   g_object_notify (G_OBJECT (status_icon), "embedded");
1578 }
1579 
1580 static void
gtk_status_icon_orientation_changed(GtkStatusIcon * status_icon)1581 gtk_status_icon_orientation_changed (GtkStatusIcon *status_icon)
1582 {
1583   gtk_status_icon_padding_changed (status_icon);
1584   g_object_notify (G_OBJECT (status_icon), "orientation");
1585 }
1586 
1587 static void
gtk_status_icon_fg_changed(GtkStatusIcon * status_icon)1588 gtk_status_icon_fg_changed (GtkStatusIcon *status_icon)
1589 {
1590   GtkStatusIconPrivate *priv = status_icon->priv;
1591   GdkRGBA *rgba;
1592 
1593   g_object_get (priv->tray_icon, "fg-color", &rgba, NULL);
1594 
1595   gtk_widget_override_color (priv->image, GTK_STATE_FLAG_NORMAL, rgba);
1596 
1597   gdk_rgba_free (rgba);
1598 }
1599 
1600 static void
gtk_status_icon_color_changed(GtkTrayIcon * tray,GParamSpec * pspec,GtkStatusIcon * status_icon)1601 gtk_status_icon_color_changed (GtkTrayIcon   *tray,
1602                                GParamSpec    *pspec,
1603                                GtkStatusIcon *status_icon)
1604 {
1605   GtkStatusIconPrivate *priv = status_icon->priv;
1606   const gchar *name;
1607 
1608   switch (pspec->name[0])
1609     {
1610     case 'e':
1611       name = "error";
1612       break;
1613     case 'w':
1614       name = "warning";
1615       break;
1616     case 's':
1617       name = "success";
1618       break;
1619     default:
1620       name = NULL;
1621       break;
1622     }
1623 
1624   if (name)
1625     {
1626       GdkRGBA rgba;
1627 
1628       g_object_get (priv->tray_icon, pspec->name, &rgba, NULL);
1629 
1630       rgba.alpha = 1;
1631 
1632       gtk_widget_override_symbolic_color (priv->image, name, &rgba);
1633     }
1634 }
1635 
1636 static gboolean
gtk_status_icon_key_press(GtkStatusIcon * status_icon,GdkEventKey * event)1637 gtk_status_icon_key_press (GtkStatusIcon  *status_icon,
1638 			   GdkEventKey    *event)
1639 {
1640   guint state, keyval;
1641 
1642   state = event->state & gtk_accelerator_get_default_mod_mask ();
1643   keyval = event->keyval;
1644   if (state == 0 &&
1645       (keyval == GDK_KEY_Return ||
1646        keyval == GDK_KEY_KP_Enter ||
1647        keyval == GDK_KEY_ISO_Enter ||
1648        keyval == GDK_KEY_space ||
1649        keyval == GDK_KEY_KP_Space))
1650     {
1651       emit_activate_signal (status_icon);
1652       return TRUE;
1653     }
1654 
1655   return FALSE;
1656 }
1657 
1658 static void
gtk_status_icon_popup_menu(GtkStatusIcon * status_icon)1659 gtk_status_icon_popup_menu (GtkStatusIcon  *status_icon)
1660 {
1661   emit_popup_menu_signal (status_icon, 0, gtk_get_current_event_time ());
1662 }
1663 
1664 #endif  /* GDK_WINDOWING_X11 */
1665 
1666 static gboolean
gtk_status_icon_button_press(GtkStatusIcon * status_icon,GdkEventButton * event)1667 gtk_status_icon_button_press (GtkStatusIcon  *status_icon,
1668 			      GdkEventButton *event)
1669 {
1670   gboolean handled = FALSE;
1671 
1672   g_signal_emit (status_icon,
1673 		 status_icon_signals [BUTTON_PRESS_EVENT_SIGNAL], 0,
1674 		 event, &handled);
1675   if (handled)
1676     return TRUE;
1677 
1678   if (gdk_event_triggers_context_menu ((GdkEvent *) event))
1679     {
1680       emit_popup_menu_signal (status_icon, event->button, event->time);
1681       return TRUE;
1682     }
1683   else if (event->button == GDK_BUTTON_PRIMARY && event->type == GDK_BUTTON_PRESS)
1684     {
1685       emit_activate_signal (status_icon);
1686       return TRUE;
1687     }
1688 
1689   return FALSE;
1690 }
1691 
1692 static gboolean
gtk_status_icon_button_release(GtkStatusIcon * status_icon,GdkEventButton * event)1693 gtk_status_icon_button_release (GtkStatusIcon  *status_icon,
1694 				GdkEventButton *event)
1695 {
1696   gboolean handled = FALSE;
1697   g_signal_emit (status_icon,
1698 		 status_icon_signals [BUTTON_RELEASE_EVENT_SIGNAL], 0,
1699 		 event, &handled);
1700   return handled;
1701 }
1702 
1703 #ifdef GDK_WINDOWING_X11
1704 
1705 static gboolean
gtk_status_icon_scroll(GtkStatusIcon * status_icon,GdkEventScroll * event)1706 gtk_status_icon_scroll (GtkStatusIcon  *status_icon,
1707 			GdkEventScroll *event)
1708 {
1709   gboolean handled = FALSE;
1710   g_signal_emit (status_icon,
1711 		 status_icon_signals [SCROLL_EVENT_SIGNAL], 0,
1712 		 event, &handled);
1713   return handled;
1714 }
1715 
1716 static gboolean
gtk_status_icon_query_tooltip(GtkStatusIcon * status_icon,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip)1717 gtk_status_icon_query_tooltip (GtkStatusIcon *status_icon,
1718 			       gint           x,
1719 			       gint           y,
1720 			       gboolean       keyboard_tip,
1721 			       GtkTooltip    *tooltip)
1722 {
1723   gboolean handled = FALSE;
1724   g_signal_emit (status_icon,
1725 		 status_icon_signals [QUERY_TOOLTIP_SIGNAL], 0,
1726 		 x, y, keyboard_tip, tooltip, &handled);
1727   return handled;
1728 }
1729 
1730 #endif /* GDK_WINDOWING_X11 */
1731 
1732 static void
gtk_status_icon_reset_image_data(GtkStatusIcon * status_icon)1733 gtk_status_icon_reset_image_data (GtkStatusIcon *status_icon)
1734 {
1735   GtkStatusIconPrivate *priv = status_icon->priv;
1736   GtkImageType storage_type = gtk_image_definition_get_storage_type (priv->image_def);
1737 
1738   switch (storage_type)
1739   {
1740     case GTK_IMAGE_PIXBUF:
1741       g_object_notify (G_OBJECT (status_icon), "pixbuf");
1742       break;
1743     case GTK_IMAGE_STOCK:
1744       g_object_notify (G_OBJECT (status_icon), "stock");
1745       break;
1746     case GTK_IMAGE_ICON_NAME:
1747       g_object_notify (G_OBJECT (status_icon), "icon-name");
1748       break;
1749     case GTK_IMAGE_GICON:
1750       g_object_notify (G_OBJECT (status_icon), "gicon");
1751       break;
1752     case GTK_IMAGE_EMPTY:
1753       break;
1754     default:
1755       g_assert_not_reached ();
1756       break;
1757   }
1758 
1759   gtk_image_definition_unref (priv->image_def);
1760   priv->image_def = gtk_image_definition_new_empty ();
1761   g_object_notify (G_OBJECT (status_icon), "storage-type");
1762 }
1763 
1764 static void
gtk_status_icon_take_image(GtkStatusIcon * status_icon,GtkImageDefinition * def)1765 gtk_status_icon_take_image (GtkStatusIcon      *status_icon,
1766                             GtkImageDefinition *def)
1767 {
1768   GtkStatusIconPrivate *priv = status_icon->priv;
1769 
1770   g_object_freeze_notify (G_OBJECT (status_icon));
1771 
1772   gtk_status_icon_reset_image_data (status_icon);
1773 
1774   g_object_notify (G_OBJECT (status_icon), "storage-type");
1775 
1776   if (def != NULL)
1777     {
1778       gtk_image_definition_unref (priv->image_def);
1779       priv->image_def = def;
1780       /* the icon size we pass here doesn't really matter, since
1781        * we force a pixel size before doing the actual rendering anyway.
1782        */
1783       switch (gtk_image_definition_get_storage_type (def))
1784         {
1785         case GTK_IMAGE_PIXBUF:
1786           g_object_notify (G_OBJECT (status_icon), "pixbuf");
1787           break;
1788         case GTK_IMAGE_STOCK:
1789           g_object_notify (G_OBJECT (status_icon), "stock");
1790           break;
1791         case GTK_IMAGE_ICON_NAME:
1792           g_object_notify (G_OBJECT (status_icon), "icon-name");
1793           break;
1794         case GTK_IMAGE_GICON:
1795           g_object_notify (G_OBJECT (status_icon), "gicon");
1796           break;
1797         default:
1798           g_warning ("Image type %u not handled by GtkStatusIcon",
1799                      gtk_image_definition_get_storage_type (def));
1800         }
1801     }
1802 
1803   g_object_thaw_notify (G_OBJECT (status_icon));
1804 
1805   gtk_status_icon_update_image (status_icon);
1806 }
1807 
1808 /**
1809  * gtk_status_icon_set_from_pixbuf:
1810  * @status_icon: a #GtkStatusIcon
1811  * @pixbuf: (allow-none): a #GdkPixbuf or %NULL
1812  *
1813  * Makes @status_icon display @pixbuf.
1814  * See gtk_status_icon_new_from_pixbuf() for details.
1815  *
1816  * Since: 2.10
1817  *
1818  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1819  *   provide status notifications; you can use g_notification_set_icon()
1820  *   to associate a #GIcon with a notification
1821  */
1822 void
gtk_status_icon_set_from_pixbuf(GtkStatusIcon * status_icon,GdkPixbuf * pixbuf)1823 gtk_status_icon_set_from_pixbuf (GtkStatusIcon *status_icon,
1824 				 GdkPixbuf     *pixbuf)
1825 {
1826   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1827   g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
1828 
1829   gtk_status_icon_take_image (status_icon,
1830       			      gtk_image_definition_new_pixbuf (pixbuf, 1));
1831 }
1832 
1833 /**
1834  * gtk_status_icon_set_from_file:
1835  * @status_icon: a #GtkStatusIcon
1836  * @filename: (type filename): a filename
1837  *
1838  * Makes @status_icon display the file @filename.
1839  * See gtk_status_icon_new_from_file() for details.
1840  *
1841  * Since: 2.10
1842  *
1843  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1844  *   provide status notifications; you can use g_notification_set_icon()
1845  *   to associate a #GIcon with a notification
1846  */
1847 void
gtk_status_icon_set_from_file(GtkStatusIcon * status_icon,const gchar * filename)1848 gtk_status_icon_set_from_file (GtkStatusIcon *status_icon,
1849  			       const gchar   *filename)
1850 {
1851   GdkPixbuf *pixbuf;
1852 
1853   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1854   g_return_if_fail (filename != NULL);
1855 
1856   pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1857 
1858   gtk_status_icon_set_from_pixbuf (status_icon, pixbuf);
1859 
1860   if (pixbuf)
1861     g_object_unref (pixbuf);
1862 }
1863 
1864 /**
1865  * gtk_status_icon_set_from_stock:
1866  * @status_icon: a #GtkStatusIcon
1867  * @stock_id: a stock icon id
1868  *
1869  * Makes @status_icon display the stock icon with the id @stock_id.
1870  * See gtk_status_icon_new_from_stock() for details.
1871  *
1872  * Since: 2.10
1873  *
1874  * Deprecated: 3.10: Use gtk_status_icon_set_from_icon_name() instead.
1875  **/
1876 void
gtk_status_icon_set_from_stock(GtkStatusIcon * status_icon,const gchar * stock_id)1877 gtk_status_icon_set_from_stock (GtkStatusIcon *status_icon,
1878 				const gchar   *stock_id)
1879 {
1880   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1881   g_return_if_fail (stock_id != NULL);
1882 
1883   gtk_status_icon_take_image (status_icon,
1884       			      gtk_image_definition_new_stock (stock_id));
1885 }
1886 
1887 /**
1888  * gtk_status_icon_set_from_icon_name:
1889  * @status_icon: a #GtkStatusIcon
1890  * @icon_name: an icon name
1891  *
1892  * Makes @status_icon display the icon named @icon_name from the
1893  * current icon theme.
1894  * See gtk_status_icon_new_from_icon_name() for details.
1895  *
1896  * Since: 2.10
1897  *
1898  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1899  *   provide status notifications; you can use g_notification_set_icon()
1900  *   to associate a #GIcon with a notification
1901  */
1902 void
gtk_status_icon_set_from_icon_name(GtkStatusIcon * status_icon,const gchar * icon_name)1903 gtk_status_icon_set_from_icon_name (GtkStatusIcon *status_icon,
1904 				    const gchar   *icon_name)
1905 {
1906   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1907   g_return_if_fail (icon_name != NULL);
1908 
1909   gtk_status_icon_take_image (status_icon,
1910       			      gtk_image_definition_new_icon_name (icon_name));
1911 }
1912 
1913 /**
1914  * gtk_status_icon_set_from_gicon:
1915  * @status_icon: a #GtkStatusIcon
1916  * @icon: a GIcon
1917  *
1918  * Makes @status_icon display the #GIcon.
1919  * See gtk_status_icon_new_from_gicon() for details.
1920  *
1921  * Since: 2.14
1922  *
1923  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1924  *   provide status notifications; you can use g_notification_set_icon()
1925  *   to associate a #GIcon with a notification
1926  */
1927 void
gtk_status_icon_set_from_gicon(GtkStatusIcon * status_icon,GIcon * icon)1928 gtk_status_icon_set_from_gicon (GtkStatusIcon *status_icon,
1929                                 GIcon         *icon)
1930 {
1931   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
1932   g_return_if_fail (icon != NULL);
1933 
1934   gtk_status_icon_take_image (status_icon,
1935       			      gtk_image_definition_new_gicon (icon));
1936 }
1937 
1938 /**
1939  * gtk_status_icon_get_storage_type:
1940  * @status_icon: a #GtkStatusIcon
1941  *
1942  * Gets the type of representation being used by the #GtkStatusIcon
1943  * to store image data. If the #GtkStatusIcon has no image data,
1944  * the return value will be %GTK_IMAGE_EMPTY.
1945  *
1946  * Returns: the image representation being used
1947  *
1948  * Since: 2.10
1949  *
1950  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1951  *   provide status notifications; there is no direct replacement
1952  *   for this function, and #GNotification only supports #GIcon
1953  *   instances
1954  */
1955 GtkImageType
gtk_status_icon_get_storage_type(GtkStatusIcon * status_icon)1956 gtk_status_icon_get_storage_type (GtkStatusIcon *status_icon)
1957 {
1958   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), GTK_IMAGE_EMPTY);
1959 
1960   return gtk_image_definition_get_storage_type (status_icon->priv->image_def);
1961 }
1962 /**
1963  * gtk_status_icon_get_pixbuf:
1964  * @status_icon: a #GtkStatusIcon
1965  *
1966  * Gets the #GdkPixbuf being displayed by the #GtkStatusIcon.
1967  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
1968  * %GTK_IMAGE_PIXBUF (see gtk_status_icon_get_storage_type()).
1969  * The caller of this function does not own a reference to the
1970  * returned pixbuf.
1971  *
1972  * Returns: (nullable) (transfer none): the displayed pixbuf,
1973  *     or %NULL if the image is empty.
1974  *
1975  * Since: 2.10
1976  *
1977  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
1978  *   provide status notifications; there is no direct replacement
1979  *   for this function
1980  */
1981 GdkPixbuf *
gtk_status_icon_get_pixbuf(GtkStatusIcon * status_icon)1982 gtk_status_icon_get_pixbuf (GtkStatusIcon *status_icon)
1983 {
1984   GtkStatusIconPrivate *priv;
1985 
1986   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
1987 
1988   priv = status_icon->priv;
1989 
1990   return gtk_image_definition_get_pixbuf (priv->image_def);
1991 }
1992 
1993 /**
1994  * gtk_status_icon_get_stock:
1995  * @status_icon: a #GtkStatusIcon
1996  *
1997  * Gets the id of the stock icon being displayed by the #GtkStatusIcon.
1998  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
1999  * %GTK_IMAGE_STOCK (see gtk_status_icon_get_storage_type()).
2000  * The returned string is owned by the #GtkStatusIcon and should not
2001  * be freed or modified.
2002  *
2003  * Returns: (nullable): stock id of the displayed stock icon,
2004  *   or %NULL if the image is empty.
2005  *
2006  * Since: 2.10
2007  *
2008  * Deprecated: 3.10: Use gtk_status_icon_get_icon_name() instead.
2009  **/
2010 const gchar *
gtk_status_icon_get_stock(GtkStatusIcon * status_icon)2011 gtk_status_icon_get_stock (GtkStatusIcon *status_icon)
2012 {
2013   GtkStatusIconPrivate *priv;
2014 
2015   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2016 
2017   priv = status_icon->priv;
2018 
2019   return gtk_image_definition_get_stock (priv->image_def);
2020 }
2021 
2022 /**
2023  * gtk_status_icon_get_icon_name:
2024  * @status_icon: a #GtkStatusIcon
2025  *
2026  * Gets the name of the icon being displayed by the #GtkStatusIcon.
2027  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
2028  * %GTK_IMAGE_ICON_NAME (see gtk_status_icon_get_storage_type()).
2029  * The returned string is owned by the #GtkStatusIcon and should not
2030  * be freed or modified.
2031  *
2032  * Returns: (nullable): name of the displayed icon, or %NULL if the image is empty.
2033  *
2034  * Since: 2.10
2035  *
2036  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2037  *   provide status notifications; there is no direct replacement
2038  *   for this function
2039  */
2040 const gchar *
gtk_status_icon_get_icon_name(GtkStatusIcon * status_icon)2041 gtk_status_icon_get_icon_name (GtkStatusIcon *status_icon)
2042 {
2043   GtkStatusIconPrivate *priv;
2044 
2045   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2046 
2047   priv = status_icon->priv;
2048 
2049   return gtk_image_definition_get_icon_name (priv->image_def);
2050 }
2051 
2052 /**
2053  * gtk_status_icon_get_gicon:
2054  * @status_icon: a #GtkStatusIcon
2055  *
2056  * Retrieves the #GIcon being displayed by the #GtkStatusIcon.
2057  * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
2058  * %GTK_IMAGE_GICON (see gtk_status_icon_get_storage_type()).
2059  * The caller of this function does not own a reference to the
2060  * returned #GIcon.
2061  *
2062  * If this function fails, @icon is left unchanged;
2063  *
2064  * Returns: (nullable) (transfer none): the displayed icon, or %NULL if the image is empty
2065  *
2066  * Since: 2.14
2067  *
2068  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2069  *   provide status notifications; there is no direct replacement
2070  *   for this function
2071  */
2072 GIcon *
gtk_status_icon_get_gicon(GtkStatusIcon * status_icon)2073 gtk_status_icon_get_gicon (GtkStatusIcon *status_icon)
2074 {
2075   GtkStatusIconPrivate *priv;
2076 
2077   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2078 
2079   priv = status_icon->priv;
2080 
2081   return gtk_image_definition_get_gicon (priv->image_def);
2082 }
2083 
2084 /**
2085  * gtk_status_icon_get_size:
2086  * @status_icon: a #GtkStatusIcon
2087  *
2088  * Gets the size in pixels that is available for the image.
2089  * Stock icons and named icons adapt their size automatically
2090  * if the size of the notification area changes. For other
2091  * storage types, the size-changed signal can be used to
2092  * react to size changes.
2093  *
2094  * Note that the returned size is only meaningful while the
2095  * status icon is embedded (see gtk_status_icon_is_embedded()).
2096  *
2097  * Returns: the size that is available for the image
2098  *
2099  * Since: 2.10
2100  *
2101  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2102  *   provide status notifications; there is no direct replacement
2103  *   for this function, as the representation of a notification
2104  *   is left to the platform
2105  */
2106 gint
gtk_status_icon_get_size(GtkStatusIcon * status_icon)2107 gtk_status_icon_get_size (GtkStatusIcon *status_icon)
2108 {
2109   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), 0);
2110 
2111   return status_icon->priv->size;
2112 }
2113 
2114 /**
2115  * gtk_status_icon_set_screen:
2116  * @status_icon: a #GtkStatusIcon
2117  * @screen: a #GdkScreen
2118  *
2119  * Sets the #GdkScreen where @status_icon is displayed; if
2120  * the icon is already mapped, it will be unmapped, and
2121  * then remapped on the new screen.
2122  *
2123  * Since: 2.12
2124  *
2125  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2126  *   provide status notifications; there is no direct replacement
2127  *   for this function, as GTK typically only has one #GdkScreen
2128  *   and notifications are managed by the platform
2129  */
2130 void
gtk_status_icon_set_screen(GtkStatusIcon * status_icon,GdkScreen * screen)2131 gtk_status_icon_set_screen (GtkStatusIcon *status_icon,
2132                             GdkScreen     *screen)
2133 {
2134   g_return_if_fail (GDK_IS_SCREEN (screen));
2135 
2136 #ifdef GDK_WINDOWING_X11
2137   if (status_icon->priv->tray_icon)
2138     gtk_window_set_screen (GTK_WINDOW (status_icon->priv->tray_icon), screen);
2139 #endif
2140 }
2141 
2142 /**
2143  * gtk_status_icon_get_screen:
2144  * @status_icon: a #GtkStatusIcon
2145  *
2146  * Returns the #GdkScreen associated with @status_icon.
2147  *
2148  * Returns: (transfer none): a #GdkScreen.
2149  *
2150  * Since: 2.12
2151  *
2152  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2153  *   provide status notifications; there is no direct replacement
2154  *   for this function, as notifications are managed by the platform
2155  */
2156 GdkScreen *
gtk_status_icon_get_screen(GtkStatusIcon * status_icon)2157 gtk_status_icon_get_screen (GtkStatusIcon *status_icon)
2158 {
2159   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2160 
2161 #ifdef GDK_WINDOWING_X11
2162   if (status_icon->priv->tray_icon)
2163     return gtk_window_get_screen (GTK_WINDOW (status_icon->priv->tray_icon));
2164   else
2165 #endif
2166   return gdk_screen_get_default ();
2167 }
2168 
2169 /**
2170  * gtk_status_icon_set_visible:
2171  * @status_icon: a #GtkStatusIcon
2172  * @visible: %TRUE to show the status icon, %FALSE to hide it
2173  *
2174  * Shows or hides a status icon.
2175  *
2176  * Since: 2.10
2177  *
2178  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2179  *   provide status notifications; there is no direct replacement
2180  *   for this function, as notifications are managed by the platform
2181  */
2182 void
gtk_status_icon_set_visible(GtkStatusIcon * status_icon,gboolean visible)2183 gtk_status_icon_set_visible (GtkStatusIcon *status_icon,
2184 			     gboolean       visible)
2185 {
2186   GtkStatusIconPrivate *priv;
2187 
2188   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
2189 
2190   priv = status_icon->priv;
2191 
2192   visible = visible != FALSE;
2193 
2194   if (priv->visible != visible)
2195     {
2196       priv->visible = visible;
2197 
2198 #ifdef GDK_WINDOWING_X11
2199       if (priv->tray_icon)
2200         {
2201           if (visible)
2202 	    gtk_widget_show (priv->tray_icon);
2203           else if (gtk_widget_get_realized (priv->tray_icon))
2204             {
2205 	      gtk_widget_hide (priv->tray_icon);
2206 	      gtk_widget_unrealize (priv->tray_icon);
2207             }
2208         }
2209 #endif
2210 #ifdef GDK_WINDOWING_WIN32
2211       if (priv->nid.hWnd != NULL)
2212 	{
2213 	  if (visible)
2214 	    Shell_NotifyIconW (NIM_ADD, &priv->nid);
2215 	  else
2216 	    Shell_NotifyIconW (NIM_DELETE, &priv->nid);
2217 	}
2218 #endif
2219 #ifdef GDK_WINDOWING_QUARTZ
2220       QUARTZ_POOL_ALLOC;
2221       [priv->status_item setVisible:visible];
2222       QUARTZ_POOL_RELEASE;
2223 #endif
2224       g_object_notify (G_OBJECT (status_icon), "visible");
2225     }
2226 }
2227 
2228 /**
2229  * gtk_status_icon_get_visible:
2230  * @status_icon: a #GtkStatusIcon
2231  *
2232  * Returns whether the status icon is visible or not.
2233  * Note that being visible does not guarantee that
2234  * the user can actually see the icon, see also
2235  * gtk_status_icon_is_embedded().
2236  *
2237  * Returns: %TRUE if the status icon is visible
2238  *
2239  * Since: 2.10
2240  *
2241  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2242  *   provide status notifications; there is no direct replacement
2243  *   for this function
2244  */
2245 gboolean
gtk_status_icon_get_visible(GtkStatusIcon * status_icon)2246 gtk_status_icon_get_visible (GtkStatusIcon *status_icon)
2247 {
2248   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
2249 
2250   return status_icon->priv->visible;
2251 }
2252 
2253 /**
2254  * gtk_status_icon_is_embedded:
2255  * @status_icon: a #GtkStatusIcon
2256  *
2257  * Returns whether the status icon is embedded in a notification
2258  * area.
2259  *
2260  * Returns: %TRUE if the status icon is embedded in
2261  *   a notification area.
2262  *
2263  * Since: 2.10
2264  *
2265  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2266  *   provide status notifications; there is no direct replacement
2267  *   for this function
2268  */
2269 gboolean
gtk_status_icon_is_embedded(GtkStatusIcon * status_icon)2270 gtk_status_icon_is_embedded (GtkStatusIcon *status_icon)
2271 {
2272   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
2273 
2274 #ifdef GDK_WINDOWING_X11
2275   if (status_icon->priv->tray_icon == NULL ||
2276       !gtk_plug_get_embedded (GTK_PLUG (status_icon->priv->tray_icon)))
2277     return FALSE;
2278 #endif
2279   return TRUE;
2280 }
2281 
2282 /**
2283  * gtk_status_icon_position_menu:
2284  * @menu: the #GtkMenu
2285  * @x: (inout): return location for the x position
2286  * @y: (inout): return location for the y position
2287  * @push_in: (out): whether the first menu item should be offset
2288  *           (pushed in) to be aligned with the menu popup position
2289  *           (only useful for GtkOptionMenu).
2290  * @user_data: (type GtkStatusIcon): the status icon to position the menu on
2291  *
2292  * Menu positioning function to use with gtk_menu_popup()
2293  * to position @menu aligned to the status icon @user_data.
2294  *
2295  * Since: 2.10
2296  *
2297  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2298  *   provide status notifications; notifications do not have menus,
2299  *   but can have buttons, and actions associated with each button
2300  */
2301 void
gtk_status_icon_position_menu(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer user_data)2302 gtk_status_icon_position_menu (GtkMenu  *menu,
2303 			       gint     *x,
2304 			       gint     *y,
2305 			       gboolean *push_in,
2306 			       gpointer  user_data)
2307 {
2308 #ifdef GDK_WINDOWING_X11
2309   GtkStatusIcon *status_icon = GTK_STATUS_ICON (user_data);
2310   GtkStatusIconPrivate *priv = status_icon->priv;
2311   GtkAllocation allocation;
2312   GtkTrayIcon *tray_icon;
2313   GtkWidget *widget;
2314   GdkScreen *screen;
2315   GtkTextDirection direction;
2316   GtkRequisition menu_req;
2317   GdkRectangle monitor;
2318   GdkWindow *window;
2319   gint monitor_num, height, width, xoffset, yoffset;
2320 
2321   g_return_if_fail (GTK_IS_MENU (menu));
2322   g_return_if_fail (GTK_IS_STATUS_ICON (user_data));
2323 
2324   if (priv->tray_icon == NULL)
2325     {
2326       *x = 0;
2327       *y = 0;
2328       return;
2329     }
2330 
2331   tray_icon = GTK_TRAY_ICON (priv->tray_icon);
2332   widget = priv->tray_icon;
2333 
2334   direction = gtk_widget_get_direction (widget);
2335 
2336   screen = gtk_widget_get_screen (widget);
2337   gtk_menu_set_screen (menu, screen);
2338 
2339   window = gtk_widget_get_window (widget);
2340   monitor_num = gdk_screen_get_monitor_at_window (screen, window);
2341   if (monitor_num < 0)
2342     monitor_num = 0;
2343   gtk_menu_set_monitor (menu, monitor_num);
2344 
2345   gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
2346 
2347   gdk_window_get_origin (window, x, y);
2348 
2349   menu_req.width = gtk_widget_get_allocated_width (GTK_WIDGET (menu));
2350   menu_req.height = gtk_widget_get_allocated_height (GTK_WIDGET (menu));
2351 
2352   gtk_widget_get_allocation (widget, &allocation);
2353   if (_gtk_tray_icon_get_orientation (tray_icon) == GTK_ORIENTATION_VERTICAL)
2354     {
2355       width = 0;
2356       height = allocation.height;
2357       xoffset = allocation.width;
2358       yoffset = 0;
2359     }
2360   else
2361     {
2362       width = allocation.width;
2363       height = 0;
2364       xoffset = 0;
2365       yoffset = allocation.height;
2366     }
2367 
2368   if (direction == GTK_TEXT_DIR_RTL)
2369     {
2370       if ((*x - (menu_req.width - width)) >= monitor.x)
2371         *x -= menu_req.width - width;
2372       else if ((*x + xoffset + menu_req.width) < (monitor.x + monitor.width))
2373         *x += xoffset;
2374       else if ((monitor.x + monitor.width - (*x + xoffset)) < *x)
2375         *x -= menu_req.width - width;
2376       else
2377         *x += xoffset;
2378     }
2379   else
2380     {
2381       if ((*x + xoffset + menu_req.width) < (monitor.x + monitor.width))
2382         *x += xoffset;
2383       else if ((*x - (menu_req.width - width)) >= monitor.x)
2384         *x -= menu_req.width - width;
2385       else if ((monitor.x + monitor.width - (*x + xoffset)) > *x)
2386         *x += xoffset;
2387       else
2388         *x -= menu_req.width - width;
2389     }
2390 
2391   if ((*y + yoffset + menu_req.height) < (monitor.y + monitor.height))
2392     *y += yoffset;
2393   else if ((*y - (menu_req.height - height)) >= monitor.y)
2394     *y -= menu_req.height - height;
2395   else if (monitor.y + monitor.height - (*y + yoffset) > *y)
2396     *y += yoffset;
2397   else
2398     *y -= menu_req.height - height;
2399 
2400   *push_in = FALSE;
2401 #endif /* GDK_WINDOWING_X11 */
2402 
2403 #ifdef GDK_WINDOWING_WIN32
2404   GtkStatusIcon *status_icon;
2405   GtkStatusIconPrivate *priv;
2406   GtkRequisition menu_req;
2407 
2408   g_return_if_fail (GTK_IS_MENU (menu));
2409   g_return_if_fail (GTK_IS_STATUS_ICON (user_data));
2410 
2411   status_icon = GTK_STATUS_ICON (user_data);
2412   priv = status_icon->priv;
2413 
2414   gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_req, NULL);
2415 
2416   *x = priv->last_click_x;
2417   *y = priv->taskbar_top - menu_req.height;
2418 
2419   *push_in = TRUE;
2420 #endif
2421 }
2422 
2423 /**
2424  * gtk_status_icon_get_geometry:
2425  * @status_icon: a #GtkStatusIcon
2426  * @screen: (out) (transfer none) (allow-none): return location for
2427  *          the screen, or %NULL if the information is not needed
2428  * @area: (out) (allow-none): return location for the area occupied by
2429  *        the status icon, or %NULL
2430  * @orientation: (out) (allow-none): return location for the
2431  *    orientation of the panel in which the status icon is embedded,
2432  *    or %NULL. A panel at the top or bottom of the screen is
2433  *    horizontal, a panel at the left or right is vertical.
2434  *
2435  * Obtains information about the location of the status icon
2436  * on screen. This information can be used to e.g. position
2437  * popups like notification bubbles.
2438  *
2439  * See gtk_status_icon_position_menu() for a more convenient
2440  * alternative for positioning menus.
2441  *
2442  * Note that some platforms do not allow GTK+ to provide
2443  * this information, and even on platforms that do allow it,
2444  * the information is not reliable unless the status icon
2445  * is embedded in a notification area, see
2446  * gtk_status_icon_is_embedded().
2447  *
2448  * Returns: %TRUE if the location information has
2449  *               been filled in
2450  *
2451  * Since: 2.10
2452  *
2453  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2454  *   provide status notifications; there is no direct replacement
2455  *   for this function, as the platform is responsible for the
2456  *   presentation of notifications
2457  */
2458 gboolean
gtk_status_icon_get_geometry(GtkStatusIcon * status_icon,GdkScreen ** screen,GdkRectangle * area,GtkOrientation * orientation)2459 gtk_status_icon_get_geometry (GtkStatusIcon    *status_icon,
2460 			      GdkScreen       **screen,
2461 			      GdkRectangle     *area,
2462 			      GtkOrientation   *orientation)
2463 {
2464 #ifdef GDK_WINDOWING_X11
2465   GtkStatusIconPrivate *priv = status_icon->priv;
2466   GtkAllocation allocation;
2467   GtkWidget *widget;
2468   gint x, y;
2469 
2470   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
2471 
2472   if (priv->tray_icon == NULL)
2473     return FALSE;
2474 
2475   widget = priv->tray_icon;
2476 
2477   if (screen)
2478     *screen = gtk_widget_get_screen (widget);
2479 
2480   if (area)
2481     {
2482       gdk_window_get_origin (gtk_widget_get_window (widget),
2483                              &x, &y);
2484 
2485       gtk_widget_get_allocation (widget, &allocation);
2486       area->x = x;
2487       area->y = y;
2488       area->width = allocation.width;
2489       area->height = allocation.height;
2490     }
2491 
2492   if (orientation)
2493     *orientation = _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (widget));
2494 
2495   return TRUE;
2496 #else
2497   return FALSE;
2498 #endif /* GDK_WINDOWING_X11 */
2499 }
2500 
2501 /**
2502  * gtk_status_icon_set_has_tooltip:
2503  * @status_icon: a #GtkStatusIcon
2504  * @has_tooltip: whether or not @status_icon has a tooltip
2505  *
2506  * Sets the has-tooltip property on @status_icon to @has_tooltip.
2507  * See #GtkStatusIcon:has-tooltip for more information.
2508  *
2509  * Since: 2.16
2510  *
2511  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2512  *   provide status notifications; there is no direct replacement
2513  *   for this function, but notifications can display an arbitrary
2514  *   amount of text using g_notification_set_body()
2515  */
2516 void
gtk_status_icon_set_has_tooltip(GtkStatusIcon * status_icon,gboolean has_tooltip)2517 gtk_status_icon_set_has_tooltip (GtkStatusIcon *status_icon,
2518 				 gboolean       has_tooltip)
2519 {
2520   GtkStatusIconPrivate *priv;
2521   gboolean changed = FALSE;
2522 
2523   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
2524 
2525   priv = status_icon->priv;
2526 
2527 #ifdef GDK_WINDOWING_X11
2528   if (priv->tray_icon)
2529     {
2530       if (gtk_widget_get_has_tooltip (priv->tray_icon) != has_tooltip)
2531         {
2532           gtk_widget_set_has_tooltip (priv->tray_icon, has_tooltip);
2533           changed = TRUE;
2534         }
2535     }
2536 #endif
2537 #ifdef GDK_WINDOWING_WIN32
2538   changed = TRUE;
2539   if (!has_tooltip && priv->tooltip_text)
2540     gtk_status_icon_set_tooltip_text (status_icon, NULL);
2541 #endif
2542 #ifdef GDK_WINDOWING_QUARTZ
2543   changed = TRUE;
2544   if (!has_tooltip && priv->tooltip_text)
2545     gtk_status_icon_set_tooltip_text (status_icon, NULL);
2546 #endif
2547 
2548   if (changed)
2549     g_object_notify (G_OBJECT (status_icon), "has-tooltip");
2550 }
2551 
2552 /**
2553  * gtk_status_icon_get_has_tooltip:
2554  * @status_icon: a #GtkStatusIcon
2555  *
2556  * Returns the current value of the has-tooltip property.
2557  * See #GtkStatusIcon:has-tooltip for more information.
2558  *
2559  * Returns: current value of has-tooltip on @status_icon.
2560  *
2561  * Since: 2.16
2562  *
2563  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2564  *   provide status notifications; there is no direct replacement
2565  *   for this function
2566  */
2567 gboolean
gtk_status_icon_get_has_tooltip(GtkStatusIcon * status_icon)2568 gtk_status_icon_get_has_tooltip (GtkStatusIcon *status_icon)
2569 {
2570   GtkStatusIconPrivate *priv;
2571   gboolean has_tooltip = FALSE;
2572 
2573   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
2574 
2575   priv = status_icon->priv;
2576 
2577 #ifdef GDK_WINDOWING_X11
2578   if (priv->tray_icon)
2579     has_tooltip = gtk_widget_get_has_tooltip (priv->tray_icon);
2580 #endif
2581 #ifdef GDK_WINDOWING_WIN32
2582   has_tooltip = (priv->tooltip_text != NULL);
2583 #endif
2584 #ifdef GDK_WINDOWING_QUARTZ
2585   has_tooltip = (priv->tooltip_text != NULL);
2586 #endif
2587 
2588   return has_tooltip;
2589 }
2590 
2591 /**
2592  * gtk_status_icon_set_tooltip_text:
2593  * @status_icon: a #GtkStatusIcon
2594  * @text: the contents of the tooltip for @status_icon
2595  *
2596  * Sets @text as the contents of the tooltip.
2597  *
2598  * This function will take care of setting #GtkStatusIcon:has-tooltip to
2599  * %TRUE and of the default handler for the #GtkStatusIcon::query-tooltip
2600  * signal.
2601  *
2602  * See also the #GtkStatusIcon:tooltip-text property and
2603  * gtk_tooltip_set_text().
2604  *
2605  * Since: 2.16
2606  *
2607  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2608  *   provide status notifications; there is no direct replacement
2609  *   for this function
2610  */
2611 void
gtk_status_icon_set_tooltip_text(GtkStatusIcon * status_icon,const gchar * text)2612 gtk_status_icon_set_tooltip_text (GtkStatusIcon *status_icon,
2613 				  const gchar   *text)
2614 {
2615   GtkStatusIconPrivate *priv;
2616 
2617   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
2618 
2619   priv = status_icon->priv;
2620 
2621 #ifdef GDK_WINDOWING_X11
2622   if (priv->tray_icon)
2623     gtk_widget_set_tooltip_text (priv->tray_icon, text);
2624 #endif
2625 #ifdef GDK_WINDOWING_WIN32
2626   if (text == NULL)
2627     priv->nid.uFlags &= ~NIF_TIP;
2628   else
2629     {
2630       WCHAR *wcs = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL);
2631 
2632       priv->nid.uFlags |= NIF_TIP;
2633       wcsncpy (priv->nid.szTip, wcs, G_N_ELEMENTS (priv->nid.szTip) - 1);
2634       priv->nid.szTip[G_N_ELEMENTS (priv->nid.szTip) - 1] = 0;
2635       g_free (wcs);
2636     }
2637   if (priv->nid.hWnd != NULL && priv->visible)
2638     if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid))
2639       g_warning (G_STRLOC ": Shell_NotifyIconW(NIM_MODIFY) failed");
2640 
2641   g_free (priv->tooltip_text);
2642   priv->tooltip_text = g_strdup (text);
2643 #endif
2644 #ifdef GDK_WINDOWING_QUARTZ
2645   QUARTZ_POOL_ALLOC;
2646   [priv->status_item setToolTip:text];
2647   QUARTZ_POOL_RELEASE;
2648 
2649   g_free (priv->tooltip_text);
2650   priv->tooltip_text = g_strdup (text);
2651 #endif
2652 }
2653 
2654 /**
2655  * gtk_status_icon_get_tooltip_text:
2656  * @status_icon: a #GtkStatusIcon
2657  *
2658  * Gets the contents of the tooltip for @status_icon.
2659  *
2660  * Returns: (nullable): the tooltip text, or %NULL. You should free the
2661  *   returned string with g_free() when done.
2662  *
2663  * Since: 2.16
2664  *
2665  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2666  *   provide status notifications; there is no direct replacement
2667  *   for this function
2668  */
2669 gchar *
gtk_status_icon_get_tooltip_text(GtkStatusIcon * status_icon)2670 gtk_status_icon_get_tooltip_text (GtkStatusIcon *status_icon)
2671 {
2672   GtkStatusIconPrivate *priv;
2673   gchar *tooltip_text = NULL;
2674 
2675   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2676 
2677   priv = status_icon->priv;
2678 
2679 #ifdef GDK_WINDOWING_X11
2680   if (priv->tray_icon)
2681     tooltip_text = gtk_widget_get_tooltip_text (priv->tray_icon);
2682 #endif
2683 #ifdef GDK_WINDOWING_WIN32
2684   if (priv->tooltip_text)
2685     tooltip_text = g_strdup (priv->tooltip_text);
2686 #endif
2687 #ifdef GDK_WINDOWING_QUARTZ
2688   if (priv->tooltip_text)
2689     tooltip_text = g_strdup (priv->tooltip_text);
2690 #endif
2691 
2692   return tooltip_text;
2693 }
2694 
2695 /**
2696  * gtk_status_icon_set_tooltip_markup:
2697  * @status_icon: a #GtkStatusIcon
2698  * @markup: (allow-none): the contents of the tooltip for @status_icon, or %NULL
2699  *
2700  * Sets @markup as the contents of the tooltip, which is marked up with
2701  *  the [Pango text markup language][PangoMarkupFormat].
2702  *
2703  * This function will take care of setting #GtkStatusIcon:has-tooltip to %TRUE
2704  * and of the default handler for the #GtkStatusIcon::query-tooltip signal.
2705  *
2706  * See also the #GtkStatusIcon:tooltip-markup property and
2707  * gtk_tooltip_set_markup().
2708  *
2709  * Since: 2.16
2710  *
2711  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2712  *   provide status notifications; there is no direct replacement
2713  *   for this function
2714  */
2715 void
gtk_status_icon_set_tooltip_markup(GtkStatusIcon * status_icon,const gchar * markup)2716 gtk_status_icon_set_tooltip_markup (GtkStatusIcon *status_icon,
2717 				    const gchar   *markup)
2718 {
2719 #ifdef GDK_WINDOWING_X11
2720   GtkStatusIconPrivate *priv;
2721 #endif
2722 #if defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
2723   gchar *text = NULL;
2724 #endif
2725 
2726   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
2727 
2728 #ifdef GDK_WINDOWING_X11
2729   priv = status_icon->priv;
2730 
2731   if (priv->tray_icon)
2732     gtk_widget_set_tooltip_markup (priv->tray_icon, markup);
2733 #endif
2734 #ifdef GDK_WINDOWING_WIN32
2735   if (markup)
2736     pango_parse_markup (markup, -1, 0, NULL, &text, NULL, NULL);
2737   gtk_status_icon_set_tooltip_text (status_icon, text);
2738   g_free (text);
2739 #endif
2740 #ifdef GDK_WINDOWING_QUARTZ
2741   if (markup)
2742     pango_parse_markup (markup, -1, 0, NULL, &text, NULL, NULL);
2743   gtk_status_icon_set_tooltip_text (status_icon, text);
2744   g_free (text);
2745 #endif
2746 }
2747 
2748 /**
2749  * gtk_status_icon_get_tooltip_markup:
2750  * @status_icon: a #GtkStatusIcon
2751  *
2752  * Gets the contents of the tooltip for @status_icon.
2753  *
2754  * Returns: (nullable): the tooltip text, or %NULL. You should free the
2755  *   returned string with g_free() when done.
2756  *
2757  * Since: 2.16
2758  *
2759  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2760  *   provide status notifications; there is no direct replacement
2761  *   for this function
2762  */
2763 gchar *
gtk_status_icon_get_tooltip_markup(GtkStatusIcon * status_icon)2764 gtk_status_icon_get_tooltip_markup (GtkStatusIcon *status_icon)
2765 {
2766   GtkStatusIconPrivate *priv;
2767   gchar *markup = NULL;
2768 
2769   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2770 
2771   priv = status_icon->priv;
2772 
2773 #ifdef GDK_WINDOWING_X11
2774   if (priv->tray_icon)
2775     markup = gtk_widget_get_tooltip_markup (priv->tray_icon);
2776 #endif
2777 #ifdef GDK_WINDOWING_WIN32
2778   if (priv->tooltip_text)
2779     markup = g_markup_escape_text (priv->tooltip_text, -1);
2780 #endif
2781 #ifdef GDK_WINDOWING_QUARTZ
2782   if (priv->tooltip_text)
2783     markup = g_markup_escape_text (priv->tooltip_text, -1);
2784 #endif
2785 
2786   return markup;
2787 }
2788 
2789 /**
2790  * gtk_status_icon_get_x11_window_id:
2791  * @status_icon: a #GtkStatusIcon
2792  *
2793  * This function is only useful on the X11/freedesktop.org platform.
2794  *
2795  * It returns a window ID for the widget in the underlying
2796  * status icon implementation.  This is useful for the Galago
2797  * notification service, which can send a window ID in the protocol
2798  * in order for the server to position notification windows
2799  * pointing to a status icon reliably.
2800  *
2801  * This function is not intended for other use cases which are
2802  * more likely to be met by one of the non-X11 specific methods, such
2803  * as gtk_status_icon_position_menu().
2804  *
2805  * Returns: An 32 bit unsigned integer identifier for the
2806  * underlying X11 Window
2807  *
2808  * Since: 2.14
2809  *
2810  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2811  *   provide status notifications; there is no direct replacement
2812  *   for this function
2813  */
2814 guint32
gtk_status_icon_get_x11_window_id(GtkStatusIcon * status_icon)2815 gtk_status_icon_get_x11_window_id (GtkStatusIcon *status_icon)
2816 {
2817 #ifdef GDK_WINDOWING_X11
2818   if (status_icon->priv->tray_icon)
2819     {
2820       gtk_widget_realize (GTK_WIDGET (status_icon->priv->tray_icon));
2821       return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (status_icon->priv->tray_icon)));
2822     }
2823   else
2824 #endif
2825   return 0;
2826 }
2827 
2828 /**
2829  * gtk_status_icon_set_title:
2830  * @status_icon: a #GtkStatusIcon
2831  * @title: the title
2832  *
2833  * Sets the title of this tray icon.
2834  * This should be a short, human-readable, localized string
2835  * describing the tray icon. It may be used by tools like screen
2836  * readers to render the tray icon.
2837  *
2838  * Since: 2.18
2839  *
2840  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2841  *   provide status notifications; you should use g_notification_set_title()
2842  *   and g_notification_set_body() to present text inside your notification
2843  */
2844 void
gtk_status_icon_set_title(GtkStatusIcon * status_icon,const gchar * title)2845 gtk_status_icon_set_title (GtkStatusIcon *status_icon,
2846                            const gchar   *title)
2847 {
2848   GtkStatusIconPrivate *priv;
2849 
2850   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
2851 
2852   priv = status_icon->priv;
2853 
2854 #ifdef GDK_WINDOWING_X11
2855   if (priv->tray_icon)
2856     gtk_window_set_title (GTK_WINDOW (priv->tray_icon), title);
2857 #endif
2858 #ifdef GDK_WINDOWING_QUARTZ
2859   g_free (priv->title);
2860   priv->title = g_strdup (title);
2861 #endif
2862 #ifdef GDK_WINDOWING_WIN32
2863   g_free (priv->title);
2864   priv->title = g_strdup (title);
2865 #endif
2866 
2867   g_object_notify (G_OBJECT (status_icon), "title");
2868 }
2869 
2870 /**
2871  * gtk_status_icon_get_title:
2872  * @status_icon: a #GtkStatusIcon
2873  *
2874  * Gets the title of this tray icon. See gtk_status_icon_set_title().
2875  *
2876  * Returns: the title of the status icon
2877  *
2878  * Since: 2.18
2879  *
2880  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2881  *   provide status notifications; there is no direct replacement
2882  *   for this function
2883  */
2884 const gchar *
gtk_status_icon_get_title(GtkStatusIcon * status_icon)2885 gtk_status_icon_get_title (GtkStatusIcon *status_icon)
2886 {
2887   GtkStatusIconPrivate *priv;
2888   const gchar *title = NULL;
2889 
2890   g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
2891 
2892   priv = status_icon->priv;
2893 
2894 #ifdef GDK_WINDOWING_X11
2895   if (priv->tray_icon)
2896     title = gtk_window_get_title (GTK_WINDOW (priv->tray_icon));
2897 #endif
2898 #ifdef GDK_WINDOWING_QUARTZ
2899   title = priv->title;
2900 #endif
2901 #ifdef GDK_WINDOWING_WIN32
2902   title = priv->title;
2903 #endif
2904 
2905  return title;
2906 }
2907 
2908 
2909 /**
2910  * gtk_status_icon_set_name:
2911  * @status_icon: a #GtkStatusIcon
2912  * @name: the name
2913  *
2914  * Sets the name of this tray icon.
2915  * This should be a string identifying this icon. It is may be
2916  * used for sorting the icons in the tray and will not be shown to
2917  * the user.
2918  *
2919  * Since: 2.20
2920  *
2921  * Deprecated: 3.14: Use #GNotification and #GtkApplication to
2922  *   provide status notifications; there is no direct replacement
2923  *   for this function, as notifications are associated with a
2924  *   unique application identifier by #GApplication
2925  */
2926 void
gtk_status_icon_set_name(GtkStatusIcon * status_icon,const gchar * name)2927 gtk_status_icon_set_name (GtkStatusIcon *status_icon,
2928                           const gchar   *name)
2929 {
2930 #ifdef GDK_WINDOWING_X11
2931   GtkStatusIconPrivate *priv;
2932 #endif
2933 
2934   g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
2935 
2936 #ifdef GDK_WINDOWING_X11
2937   priv = status_icon->priv;
2938 
2939   if (priv->tray_icon)
2940     {
2941       if (gtk_widget_get_realized (priv->tray_icon))
2942         {
2943           /* gtk_window_set_wmclass() only operates on non-realized windows,
2944            * so temporarily unrealize the tray here
2945            */
2946           gtk_widget_hide (priv->tray_icon);
2947           gtk_widget_unrealize (priv->tray_icon);
2948           gtk_window_set_wmclass (GTK_WINDOW (priv->tray_icon), name, name);
2949           gtk_widget_show (priv->tray_icon);
2950         }
2951       else
2952         gtk_window_set_wmclass (GTK_WINDOW (priv->tray_icon), name, name);
2953     }
2954 #endif
2955 }
2956