1 /*
2 An object to represent the application as an application indicator
3 in the system panel.
4 
5 Copyright 2009 Canonical Ltd.
6 
7 Authors:
8     Ted Gould <ted@canonical.com>
9     Cody Russell <cody.russell@canonical.com>
10 
11 This program is free software: you can redistribute it and/or modify it
12 under the terms of either or both of the following licenses:
13 
14 1) the GNU Lesser General Public License version 3, as published by the
15    Free Software Foundation; and/or
16 2) the GNU Lesser General Public License version 2.1, as published by
17    the Free Software Foundation.
18 
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranties of
21 MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
22 PURPOSE.  See the applicable version of the GNU Lesser General Public
23 License for more details.
24 
25 You should have received a copy of both the GNU Lesser General Public
26 License version 3 and version 2.1 along with this program.  If not, see
27 <http://www.gnu.org/licenses/>
28 */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include <libdbusmenu-glib/menuitem.h>
35 #include <libdbusmenu-glib/server.h>
36 #include <libdbusmenu-gtk/client.h>
37 #include <libdbusmenu-gtk/parser.h>
38 
39 #include "indicator-desktop-shortcuts.h"
40 
41 #include <stdlib.h>
42 
43 #include "app-indicator.h"
44 #include "app-indicator-enum-types.h"
45 #include "application-service-marshal.h"
46 
47 #include "gen-notification-watcher.xml.h"
48 #include "gen-notification-item.xml.h"
49 
50 #include "dbus-shared.h"
51 #include "generate-id.h"
52 
53 #define PANEL_ICON_SUFFIX  "panel"
54 
55 /**
56  * AppIndicatorPrivate:
57  * All of the private data in an instance of an application indicator.
58  *
59  * Private Fields
60  * @id: The ID of the indicator.  Maps to AppIndicator:id.
61  * @category: Which category the indicator is.  Maps to AppIndicator:category.
62  * @status: The status of the indicator.  Maps to AppIndicator:status.
63  * @icon_name: The name of the icon to use.  Maps to AppIndicator:icon-name.
64  * @attention_icon_name: The name of the attention icon to use.  Maps to AppIndicator:attention-icon-name.
65  * @menu: The menu for this indicator.  Maps to AppIndicator:menu
66  * @watcher_proxy: The proxy connection to the watcher we're connected to.  If we're not connected to one this will be %NULL.
67  */
68 struct _AppIndicatorPrivate {
69 	/*< Private >*/
70 	/* Properties */
71 	gchar                *id;
72 	gchar                *clean_id;
73 	AppIndicatorCategory  category;
74 	AppIndicatorStatus    status;
75 	gchar                *icon_name;
76 	gchar                *absolute_icon_name;
77 	gchar                *attention_icon_name;
78 	gchar                *absolute_attention_icon_name;
79 	gchar                *icon_theme_path;
80 	gchar                *absolute_icon_theme_path;
81 	DbusmenuServer       *menuservice;
82 	GtkWidget            *menu;
83 	GtkWidget            *sec_activate_target;
84 	gboolean              sec_activate_enabled;
85 	guint32               ordering_index;
86 	gchar *               title;
87 	gchar *               label;
88 	gchar *               label_guide;
89 	gchar *               accessible_desc;
90 	gchar *               att_accessible_desc;
91 	guint                 label_change_idle;
92 
93 	GtkStatusIcon *       status_icon;
94 	gint                  fallback_timer;
95 
96 	/* Fun stuff */
97 	GDBusConnection      *connection;
98 	guint                 dbus_registration;
99 	gchar *               path;
100 
101 	/* StatusNotifierWatcher */
102 	GDBusProxy           *watcher_proxy;
103 	guint                 watcher_id;
104 
105 	/* Might be used */
106 	IndicatorDesktopShortcuts * shorties;
107 };
108 
109 /* Signals Stuff */
110 enum {
111 	NEW_ICON,
112 	NEW_ATTENTION_ICON,
113 	NEW_STATUS,
114 	NEW_LABEL,
115 	CONNECTION_CHANGED,
116 	NEW_ICON_THEME_PATH,
117 	SCROLL_EVENT,
118 	LAST_SIGNAL
119 };
120 
121 static guint signals[LAST_SIGNAL] = { 0 };
122 
123 /* Enum for the properties so that they can be quickly
124    found and looked up. */
125 enum {
126 	PROP_0,
127 	PROP_ID,
128 	PROP_CATEGORY,
129 	PROP_STATUS,
130 	PROP_ICON_NAME,
131 	PROP_ICON_DESC,
132 	PROP_ATTENTION_ICON_NAME,
133 	PROP_ATTENTION_ICON_DESC,
134 	PROP_ICON_THEME_PATH,
135 	PROP_CONNECTED,
136 	PROP_LABEL,
137 	PROP_LABEL_GUIDE,
138 	PROP_ORDERING_INDEX,
139 	PROP_DBUS_MENU_SERVER,
140 	PROP_TITLE
141 };
142 
143 /* The strings so that they can be slowly looked up. */
144 #define PROP_ID_S                    "id"
145 #define PROP_CATEGORY_S              "category"
146 #define PROP_STATUS_S                "status"
147 #define PROP_ICON_NAME_S             "icon-name"
148 #define PROP_ICON_DESC_S             "icon-desc"
149 #define PROP_ATTENTION_ICON_NAME_S   "attention-icon-name"
150 #define PROP_ATTENTION_ICON_DESC_S   "attention-icon-desc"
151 #define PROP_ICON_THEME_PATH_S       "icon-theme-path"
152 #define PROP_CONNECTED_S             "connected"
153 #define PROP_LABEL_S                 "label"
154 #define PROP_LABEL_GUIDE_S           "label-guide"
155 #define PROP_ORDERING_INDEX_S        "ordering-index"
156 #define PROP_DBUS_MENU_SERVER_S      "dbus-menu-server"
157 #define PROP_TITLE_S                 "title"
158 
159 /* Private macro, shhhh! */
160 #define APP_INDICATOR_GET_PRIVATE(o) \
161                              (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_INDICATOR_TYPE, AppIndicatorPrivate))
162 
163 /* Default Path */
164 #define DEFAULT_ITEM_PATH   "/org/ayatana/NotificationItem"
165 
166 /* More constants */
167 #define DEFAULT_FALLBACK_TIMER  100 /* in milliseconds */
168 
169 /* Globals */
170 static GDBusNodeInfo *            item_node_info = NULL;
171 static GDBusInterfaceInfo *       item_interface_info = NULL;
172 static GDBusNodeInfo *            watcher_node_info = NULL;
173 static GDBusInterfaceInfo *       watcher_interface_info = NULL;
174 
175 /* Boiler plate */
176 static void app_indicator_class_init (AppIndicatorClass *klass);
177 static void app_indicator_init       (AppIndicator *self);
178 static void app_indicator_dispose    (GObject *object);
179 static void app_indicator_finalize   (GObject *object);
180 /* Property functions */
181 static void app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
182 static void app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
183 /* Other stuff */
184 static void signal_label_change (AppIndicator * self);
185 static void check_connect (AppIndicator * self);
186 static void register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data);
187 static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout);
188 static gboolean fallback_timer_expire (gpointer data);
189 static GtkStatusIcon * fallback (AppIndicator * self);
190 static void status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data);
191 static gboolean scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer user_data);
192 static gboolean middle_click_wrapper(GtkWidget *status_icon, GdkEventButton *event, gpointer user_data);
193 static void status_icon_changes (AppIndicator * self, gpointer data);
194 static void status_icon_activate (GtkStatusIcon * icon, gpointer data);
195 static void status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data);
196 static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon);
197 static gchar * append_panel_icon_suffix (const gchar * icon_name);
198 static gchar * get_real_theme_path (AppIndicator * self);
199 static gchar * append_snap_prefix (const gchar * path);
200 static void theme_changed_cb (GtkIconTheme * theme, gpointer user_data);
201 static void sec_activate_target_parent_changed(GtkWidget *menuitem, GtkWidget *old_parent, gpointer   user_data);
202 static GVariant * bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data);
203 static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data);
204 static void bus_creation (GObject * obj, GAsyncResult * res, gpointer user_data);
205 
206 static const GDBusInterfaceVTable item_interface_table = {
207 	method_call:    bus_method_call,
208 	get_property:   bus_get_prop,
209 	set_property:   NULL /* No properties that can be set */
210 };
211 
212 /* GObject type */
213 G_DEFINE_TYPE (AppIndicator, app_indicator, G_TYPE_OBJECT);
214 
215 static void
check_is_host_registered(AppIndicator * self)216 check_is_host_registered (AppIndicator *self)
217 {
218 	GVariant *variant;
219 	gboolean is_host_registered;
220 
221 	variant = g_dbus_proxy_get_cached_property (self->priv->watcher_proxy,
222 	                                            "IsStatusNotifierHostRegistered");
223 
224 	is_host_registered = FALSE;
225 	if (variant != NULL) {
226 		is_host_registered = g_variant_get_boolean (variant);
227 		g_variant_unref (variant);
228 	}
229 
230 	if (!is_host_registered) {
231 		start_fallback_timer (self, FALSE);
232 		return;
233 	}
234 
235 	check_connect (self);
236 }
237 
238 static void
watcher_properties_changed_cb(GDBusProxy * proxy,GVariant * changed_properties,GStrv invalidated_properties,AppIndicator * self)239 watcher_properties_changed_cb (GDBusProxy   *proxy,
240                                GVariant     *changed_properties,
241                                GStrv         invalidated_properties,
242                                AppIndicator *self)
243 {
244 	check_is_host_registered (self);
245 }
246 
247 static void
watcher_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)248 watcher_ready_cb (GObject      *source_object,
249                   GAsyncResult *res,
250                   gpointer      user_data)
251 {
252 	AppIndicator *self = (AppIndicator *) user_data;
253 	GError *error = NULL;
254 
255 	self->priv->watcher_proxy = g_dbus_proxy_new_finish (res, &error);
256 
257 	if (error) {
258 		start_fallback_timer (self, FALSE);
259 		g_object_unref (self);
260 
261 		g_error_free (error);
262 		return;
263 	}
264 
265 	g_signal_connect (self->priv->watcher_proxy,
266 	                  "g-properties-changed",
267 	                  G_CALLBACK (watcher_properties_changed_cb),
268 	                  self);
269 
270 	check_is_host_registered (self);
271 	g_object_unref (self);
272 }
273 
274 static void
name_appeared_handler(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)275 name_appeared_handler (GDBusConnection *connection,
276                        const gchar     *name,
277                        const gchar     *name_owner,
278                        gpointer         user_data)
279 {
280 	AppIndicator *self = (AppIndicator *) user_data;
281 
282 	g_dbus_proxy_new (self->priv->connection,
283 	                  G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
284 	                  watcher_interface_info,
285 	                  NOTIFICATION_WATCHER_DBUS_ADDR,
286 	                  NOTIFICATION_WATCHER_DBUS_OBJ,
287 	                  NOTIFICATION_WATCHER_DBUS_IFACE,
288 	                  NULL,
289 	                  (GAsyncReadyCallback) watcher_ready_cb,
290 	                  g_object_ref (self));
291 }
292 
293 static void
name_vanished_handler(GDBusConnection * connection,const gchar * name,gpointer user_data)294 name_vanished_handler (GDBusConnection *connection,
295                        const gchar     *name,
296                        gpointer         user_data)
297 {
298 	AppIndicator *self = (AppIndicator *) user_data;
299 
300 	g_clear_object (&self->priv->watcher_proxy);
301 
302 	/* Emit the AppIndicator::connection-changed signal*/
303 	g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
304 
305 	start_fallback_timer (self, FALSE);
306 }
307 
308 static void
app_indicator_class_init(AppIndicatorClass * klass)309 app_indicator_class_init (AppIndicatorClass *klass)
310 {
311 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
312 
313 	g_type_class_add_private (klass, sizeof (AppIndicatorPrivate));
314 
315 	/* Clean up */
316 	object_class->dispose = app_indicator_dispose;
317 	object_class->finalize = app_indicator_finalize;
318 
319 	/* Property funcs */
320 	object_class->set_property = app_indicator_set_property;
321 	object_class->get_property = app_indicator_get_property;
322 
323 	/* Our own funcs */
324 	klass->fallback = fallback;
325 	klass->unfallback = unfallback;
326 
327 	/* Properties */
328 
329 	/**
330 	 * AppIndicator:id:
331 	 *
332 	 * The ID for this indicator, which should be unique, but used consistently
333 	 * by this program and its indicator.
334 	 */
335 	g_object_class_install_property (object_class,
336                                          PROP_ID,
337                                          g_param_spec_string(PROP_ID_S,
338                                                              "The ID for this indicator",
339                                                              "An ID that should be unique, but used consistently by this program and its indicator.",
340                                                              NULL,
341                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
342 
343 	/**
344 	 * AppIndicator:category:
345 	 *
346 	 * The type of indicator that this represents.  Please don't use 'Other'.
347 	 * Defaults to 'ApplicationStatus'.
348 	 */
349 	g_object_class_install_property (object_class,
350                                          PROP_CATEGORY,
351                                          g_param_spec_string (PROP_CATEGORY_S,
352                                                               "Indicator Category",
353                                                               "The type of indicator that this represents.  Please don't use 'other'. Defaults to 'ApplicationStatus'.",
354                                                               NULL,
355                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
356 
357 	/**
358 	 * AppIndicator:status:
359 	 *
360 	 * Whether the indicator is shown or requests attention. Defaults to
361 	 * 'Passive'.
362 	 */
363 	g_object_class_install_property (object_class,
364                                          PROP_STATUS,
365                                          g_param_spec_string (PROP_STATUS_S,
366                                                               "Indicator Status",
367                                                               "Whether the indicator is shown or requests attention. Defaults to 'Passive'.",
368                                                               NULL,
369                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
370 
371 	/**
372 	 * AppIndicator:icon-name:
373 	 *
374 	 * The name of the regular icon that is shown for the indicator.
375 	 */
376 	g_object_class_install_property(object_class,
377 	                                PROP_ICON_NAME,
378 	                                g_param_spec_string (PROP_ICON_NAME_S,
379 	                                                     "An icon for the indicator",
380 	                                                     "The default icon that is shown for the indicator.",
381 	                                                     NULL,
382 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
383 	/**
384 	 * AppIndicator:icon-desc:
385 	 *
386 	 * The description of the regular icon that is shown for the indicator.
387 	 */
388 	g_object_class_install_property(object_class,
389 	                                PROP_ICON_DESC,
390 	                                g_param_spec_string (PROP_ICON_DESC_S,
391 	                                                     "A description of the icon for the indicator",
392 	                                                     "A description of the default icon that is shown for the indicator.",
393 	                                                     NULL,
394 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
395 
396 	/**
397 	 * AppIndicator:attention-icon-name:
398 	 *
399 	 * If the indicator sets it's status to %APP_INDICATOR_STATUS_ATTENTION
400 	 * then this icon is shown.
401 	 */
402 	g_object_class_install_property (object_class,
403                                          PROP_ATTENTION_ICON_NAME,
404                                          g_param_spec_string (PROP_ATTENTION_ICON_NAME_S,
405                                                               "An icon to show when the indicator request attention.",
406                                                               "If the indicator sets it's status to 'attention' then this icon is shown.",
407                                                               NULL,
408                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
409 	/**
410 	 * AppIndicator:attention-icon-desc:
411 	 *
412 	 * If the indicator sets it's status to %APP_INDICATOR_STATUS_ATTENTION
413 	 * then this textual description of the icon shown.
414 	 */
415 	g_object_class_install_property (object_class,
416 	                                 PROP_ATTENTION_ICON_DESC,
417 	                                 g_param_spec_string (PROP_ATTENTION_ICON_DESC_S,
418 	                                                      "A description of the icon to show when the indicator request attention.",
419 	                                                      "When the indicator is an attention mode this should describe the icon shown",
420 	                                                      NULL,
421 	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
422 	/**
423 	 * AppIndicator:icon-theme-path:
424 	 *
425 	 * An additional place to look for icon names that may be installed by the
426 	 * application.
427 	 */
428 	g_object_class_install_property(object_class,
429 	                                PROP_ICON_THEME_PATH,
430 	                                g_param_spec_string (PROP_ICON_THEME_PATH_S,
431                                                              "An additional path for custom icons.",
432                                                              "An additional place to look for icon names that may be installed by the application.",
433                                                              NULL,
434                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
435 
436 	/**
437 	 * AppIndicator:connected:
438 	 *
439 	 * Pretty simple, %TRUE if we have a reasonable expectation of being
440 	 * displayed through this object. You should hide your TrayIcon if so.
441 	 */
442 	g_object_class_install_property (object_class,
443                                          PROP_CONNECTED,
444                                          g_param_spec_boolean (PROP_CONNECTED_S,
445                                                                "Whether we're conneced to a watcher",
446                                                                "Pretty simple, true if we have a reasonable expectation of being displayed through this object.  You should hide your TrayIcon if so.",
447                                                                FALSE,
448                                                                G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
449 	/**
450 	 * AppIndicator:label:
451 	 *
452 	 * A label that can be shown next to the string in the application
453 	 * indicator.  The label will not be shown unless there is an icon
454 	 * as well.  The label is useful for numerical and other frequently
455 	 * updated information.  In general, it shouldn't be shown unless a
456 	 * user requests it as it can take up a significant amount of space
457 	 * on the user's panel.  This may not be shown in all visualizations.
458 	 */
459 	g_object_class_install_property(object_class,
460 	                                PROP_LABEL,
461 	                                g_param_spec_string (PROP_LABEL_S,
462 	                                                     "A label next to the icon",
463 	                                                     "A label to provide dynamic information.",
464 	                                                     NULL,
465 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
466 	/**
467 	 * AppIndicator:label-guide:
468 	 *
469 	 * An optional string to provide guidance to the panel on how big
470 	 * the #AppIndicator:label string could get.  If this is set correctly
471 	 * then the panel should never 'jiggle' as the string adjusts through
472 	 * out the range of options.  For instance, if you were providing a
473 	 * percentage like "54% thrust" in #AppIndicator:label you'd want to
474 	 * set this string to "100% thrust" to ensure space when Scotty can
475 	 * get you enough power.
476 	 */
477 	g_object_class_install_property(object_class,
478 	                                PROP_LABEL_GUIDE,
479 	                                g_param_spec_string (PROP_LABEL_GUIDE_S,
480 	                                                     "A string to size the space available for the label.",
481 	                                                     "To ensure that the label does not cause the panel to 'jiggle' this string should provide information on how much space it could take.",
482 	                                                     NULL,
483 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
484 	/**
485 	 * AppIndicator:ordering-index:
486 	 *
487 	 * The ordering index is an odd parameter, and if you think you don't need
488 	 * it you're probably right.  In general, the application indicator try
489 	 * to place the applications in a recreatable place taking into account
490 	 * which category they're in to try and group them.  But, there are some
491 	 * cases where you'd want to ensure indicators are next to each other.
492 	 * To do that you can override the generated ordering index and replace it
493 	 * with a new one.  Again, you probably don't want to be doing this, but
494 	 * in case you do, this is the way.
495 	 */
496 	g_object_class_install_property(object_class,
497 	                                PROP_ORDERING_INDEX,
498 	                                g_param_spec_uint (PROP_ORDERING_INDEX_S,
499 	                                                   "The location that this app indicator should be in the list.",
500 	                                                   "A way to override the default ordering of the applications by providing a very specific idea of where this entry should be placed.",
501 	                                                   0, G_MAXUINT32, 0,
502 	                                                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
503 
504 	/**
505 	 * AppIndicator:dbus-menu-server:
506 	 *
507 	 * A way to get the internal dbusmenu server if it is available.
508 	 * This should only be used for testing.
509 	 */
510 	g_object_class_install_property(object_class,
511 	                                PROP_DBUS_MENU_SERVER,
512 	                                g_param_spec_object (PROP_DBUS_MENU_SERVER_S,
513 	                                                     "The internal DBusmenu Server",
514 	                                                     "DBusmenu server which is available for testing the application indicators.",
515 	                                                     DBUSMENU_TYPE_SERVER,
516 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
517 	/**
518 	 * AppIndicator:title:
519 	 *
520 	 * Provides a way to refer to this application indicator in a human
521 	 * readable form.  This is used in the Unity desktop in the HUD as
522 	 * the first part of the menu entries to distinguish them from the
523 	 * focused application's entries.
524 	 */
525 	g_object_class_install_property(object_class,
526 	                                PROP_TITLE,
527 	                                g_param_spec_string (PROP_TITLE_S,
528 	                                                     "Title of the application indicator",
529 	                                                     "A human readable way to refer to this application indicator in the UI.",
530 	                                                     NULL,
531 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
532 
533 	/* Signals */
534 
535 	/**
536 	 * AppIndicator::new-icon:
537 	 * @arg0: The #AppIndicator object
538 	 *
539 	 * when #AppIndicator:icon-name is changed
540 	 */
541 	signals[NEW_ICON] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON,
542 	                                  G_TYPE_FROM_CLASS(klass),
543 	                                  G_SIGNAL_RUN_LAST,
544 	                                  G_STRUCT_OFFSET (AppIndicatorClass, new_icon),
545 	                                  NULL, NULL,
546 	                                  g_cclosure_marshal_VOID__VOID,
547 	                                  G_TYPE_NONE, 0);
548 
549 	/**
550 	 * AppIndicator::new-attention-icon:
551 	 * @arg0: The #AppIndicator object
552 	 *
553 	 * Emitted when #AppIndicator:attention-icon-name is changed
554 	 */
555 	signals[NEW_ATTENTION_ICON] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON,
556 	                                            G_TYPE_FROM_CLASS(klass),
557 	                                            G_SIGNAL_RUN_LAST,
558 	                                            G_STRUCT_OFFSET (AppIndicatorClass, new_attention_icon),
559 	                                            NULL, NULL,
560 	                                            g_cclosure_marshal_VOID__VOID,
561 	                                            G_TYPE_NONE, 0);
562 
563 	/**
564 	 * AppIndicator::new-status:
565 	 * @arg0: The #AppIndicator object
566 	 * @arg1: The string value of the #AppIndicatorStatus enum.
567 	 *
568 	 * Emitted when #AppIndicator:status is changed
569 	 */
570 	signals[NEW_STATUS] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_STATUS,
571 	                                    G_TYPE_FROM_CLASS(klass),
572 	                                    G_SIGNAL_RUN_LAST,
573 	                                    G_STRUCT_OFFSET (AppIndicatorClass, new_status),
574 	                                    NULL, NULL,
575 	                                    g_cclosure_marshal_VOID__STRING,
576 	                                    G_TYPE_NONE, 1,
577                                             G_TYPE_STRING);
578 
579 	/**
580 	 * AppIndicator::new-label:
581 	 * @arg0: The #AppIndicator object
582 	 * @arg1: The string for the label
583 	 * @arg1: The string for the guide
584 	 *
585 	 * Emitted when either #AppIndicator:label or #AppIndicator:label-guide are
586 	 * changed.
587 	*/
588 	signals[NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_LABEL,
589 	                                    G_TYPE_FROM_CLASS(klass),
590 	                                    G_SIGNAL_RUN_LAST,
591 	                                    G_STRUCT_OFFSET (AppIndicatorClass, new_label),
592 	                                    NULL, NULL,
593 	                                    _application_service_marshal_VOID__STRING_STRING,
594 	                                    G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
595 
596 	/**
597 	 * AppIndicator::connection-changed:
598 	 * @arg0: The #AppIndicator object
599 	 * @arg1: Whether we're connected or not
600 	 *
601 	 * Signaled when we connect to a watcher, or when it drops away.
602 	 */
603 	signals[CONNECTION_CHANGED] = g_signal_new (APP_INDICATOR_SIGNAL_CONNECTION_CHANGED,
604 	                                            G_TYPE_FROM_CLASS(klass),
605 	                                            G_SIGNAL_RUN_LAST,
606 	                                            G_STRUCT_OFFSET (AppIndicatorClass, connection_changed),
607 	                                            NULL, NULL,
608 	                                            g_cclosure_marshal_VOID__BOOLEAN,
609 	                                            G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
610 
611 	/**
612 	 * AppIndicator::new-icon-theme-path:
613 	 * @arg0: The #AppIndicator object
614 	 *
615 	 * Signaled when there is a new icon set for the
616 	 * object.
617 	 */
618 	signals[NEW_ICON_THEME_PATH] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH,
619 	                                  G_TYPE_FROM_CLASS(klass),
620 	                                  G_SIGNAL_RUN_LAST,
621 	                                  G_STRUCT_OFFSET (AppIndicatorClass, new_icon_theme_path),
622 	                                  NULL, NULL,
623 	                                  g_cclosure_marshal_VOID__STRING,
624 	                                  G_TYPE_NONE, 1, G_TYPE_STRING);
625 
626 	/**
627 	 * AppIndicator::scroll-event:
628 	 * @arg0: The #AppIndicator object
629 	 * @arg1: How many steps the scroll wheel has taken
630 	 * @arg2: (type Gdk.ScrollDirection): Which direction the wheel went in
631 	 *
632 	 * Signaled when the #AppIndicator receives a scroll event.
633 	 */
634 	signals[SCROLL_EVENT] = g_signal_new (APP_INDICATOR_SIGNAL_SCROLL_EVENT,
635 	                                  G_TYPE_FROM_CLASS(klass),
636 	                                  G_SIGNAL_RUN_LAST,
637 	                                  G_STRUCT_OFFSET (AppIndicatorClass, scroll_event),
638 	                                  NULL, NULL,
639 	                                  _application_service_marshal_VOID__INT_UINT,
640 	                                  G_TYPE_NONE, 2, G_TYPE_INT, GDK_TYPE_SCROLL_DIRECTION);
641 
642 	/* DBus interfaces */
643 	if (item_node_info == NULL) {
644 		GError * error = NULL;
645 
646 		item_node_info = g_dbus_node_info_new_for_xml(_notification_item, &error);
647 		if (error != NULL) {
648 			g_error("Unable to parse Notification Item DBus interface: %s", error->message);
649 			g_error_free(error);
650 		}
651 	}
652 
653 	if (item_interface_info == NULL && item_node_info != NULL) {
654 		item_interface_info = g_dbus_node_info_lookup_interface(item_node_info, NOTIFICATION_ITEM_DBUS_IFACE);
655 
656 		if (item_interface_info == NULL) {
657 			g_error("Unable to find interface '" NOTIFICATION_ITEM_DBUS_IFACE "'");
658 		}
659 	}
660 
661 	if (watcher_node_info == NULL) {
662 		GError * error = NULL;
663 
664 		watcher_node_info = g_dbus_node_info_new_for_xml(_notification_watcher, &error);
665 		if (error != NULL) {
666 			g_error("Unable to parse Notification Item DBus interface: %s", error->message);
667 			g_error_free(error);
668 		}
669 	}
670 
671 	if (watcher_interface_info == NULL && watcher_node_info != NULL) {
672 		watcher_interface_info = g_dbus_node_info_lookup_interface(watcher_node_info, NOTIFICATION_WATCHER_DBUS_IFACE);
673 
674 		if (watcher_interface_info == NULL) {
675 			g_error("Unable to find interface '" NOTIFICATION_WATCHER_DBUS_IFACE "'");
676 		}
677 	}
678 
679 	return;
680 }
681 
682 static void
app_indicator_init(AppIndicator * self)683 app_indicator_init (AppIndicator *self)
684 {
685 	AppIndicatorPrivate * priv = APP_INDICATOR_GET_PRIVATE(self);
686 	self->priv = priv;
687 
688 	priv->id = NULL;
689 	priv->clean_id = NULL;
690 	priv->category = APP_INDICATOR_CATEGORY_OTHER;
691 	priv->status = APP_INDICATOR_STATUS_PASSIVE;
692 	priv->icon_name = NULL;
693 	priv->attention_icon_name = NULL;
694 	priv->icon_theme_path = NULL;
695 	priv->absolute_icon_theme_path = get_real_theme_path (self);
696 	priv->menu = NULL;
697 	priv->menuservice = NULL;
698 	priv->ordering_index = 0;
699 	priv->title = NULL;
700 	priv->label = NULL;
701 	priv->label_guide = NULL;
702 	priv->label_change_idle = 0;
703 
704 	priv->connection = NULL;
705 	priv->dbus_registration = 0;
706 	priv->path = NULL;
707 
708 	priv->status_icon = NULL;
709 	priv->fallback_timer = 0;
710 
711 	priv->shorties = NULL;
712 
713 	priv->sec_activate_target = NULL;
714 	priv->sec_activate_enabled = FALSE;
715 
716 	priv->watcher_proxy = NULL;
717 	priv->watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
718 	                                     NOTIFICATION_WATCHER_DBUS_ADDR,
719 	                                     G_BUS_NAME_WATCHER_FLAGS_NONE,
720 	                                     (GBusNameAppearedCallback) name_appeared_handler,
721 	                                     (GBusNameVanishedCallback) name_vanished_handler,
722 	                                     self, NULL);
723 
724 	/* Start getting the session bus */
725 	g_object_ref(self); /* ref for the bus creation callback */
726 	g_bus_get(G_BUS_TYPE_SESSION, NULL, bus_creation, self);
727 
728 	g_signal_connect(G_OBJECT(gtk_icon_theme_get_default()),
729 		"changed", G_CALLBACK(theme_changed_cb), self);
730 
731 	return;
732 }
733 
734 /* Free all objects, make sure that all the dbus
735    signals are sent out before we shut this down. */
736 static void
app_indicator_dispose(GObject * object)737 app_indicator_dispose (GObject *object)
738 {
739 	AppIndicator *self = APP_INDICATOR (object);
740 	AppIndicatorPrivate *priv = self->priv;
741 
742 	if (priv->shorties != NULL) {
743 		g_object_unref(G_OBJECT(priv->shorties));
744 		priv->shorties = NULL;
745 	}
746 
747 	if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
748 		app_indicator_set_status(self, APP_INDICATOR_STATUS_PASSIVE);
749 	}
750 
751 	if (priv->status_icon != NULL) {
752 		AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(object);
753 		if (class->unfallback != NULL) {
754 			class->unfallback(self, priv->status_icon);
755 		}
756 		priv->status_icon = NULL;
757 	}
758 
759 	if (priv->fallback_timer != 0) {
760 		g_source_remove(priv->fallback_timer);
761 		priv->fallback_timer = 0;
762 	}
763 
764 	if (priv->label_change_idle != 0) {
765 		g_source_remove(priv->label_change_idle);
766 		priv->label_change_idle = 0;
767 	}
768 
769 	if (priv->menu != NULL) {
770 		g_object_unref(G_OBJECT(priv->menu));
771 		priv->menu = NULL;
772 	}
773 
774 	if (priv->menuservice != NULL) {
775 		g_object_unref (priv->menuservice);
776 	}
777 
778 	if (priv->watcher_id != 0) {
779 		g_bus_unwatch_name (priv->watcher_id);
780 		priv->watcher_id = 0;
781 	}
782 
783 	if (priv->watcher_proxy != NULL) {
784 		g_object_unref(G_OBJECT(priv->watcher_proxy));
785 		priv->watcher_proxy = NULL;
786 
787 	    /* Emit the AppIndicator::connection-changed signal*/
788         g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
789 	}
790 
791 	if (priv->dbus_registration != 0) {
792 		g_dbus_connection_unregister_object(priv->connection, priv->dbus_registration);
793 		priv->dbus_registration = 0;
794 	}
795 
796 	if (priv->connection != NULL) {
797 		g_object_unref(G_OBJECT(priv->connection));
798 		priv->connection = NULL;
799 	}
800 
801 	if (priv->sec_activate_target != NULL) {
802 		g_signal_handlers_disconnect_by_func (priv->sec_activate_target, sec_activate_target_parent_changed, self);
803 		g_object_unref(G_OBJECT(priv->sec_activate_target));
804 		priv->sec_activate_target = NULL;
805 	}
806 
807 	g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), self);
808 
809 	G_OBJECT_CLASS (app_indicator_parent_class)->dispose (object);
810 	return;
811 }
812 
813 /* Free all of the memory that we could be using in
814    the object. */
815 static void
app_indicator_finalize(GObject * object)816 app_indicator_finalize (GObject *object)
817 {
818 	AppIndicator * self = APP_INDICATOR(object);
819 	AppIndicatorPrivate *priv = self->priv;
820 
821 	if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
822 		g_warning("Finalizing Application Status with the status set to: %d", priv->status);
823 	}
824 
825 	if (priv->id != NULL) {
826 		g_free(priv->id);
827 		priv->id = NULL;
828 	}
829 
830 	if (priv->clean_id != NULL) {
831 		g_free(priv->clean_id);
832 		priv->clean_id = NULL;
833 	}
834 
835 	if (priv->icon_name != NULL) {
836 		g_free(priv->icon_name);
837 		priv->icon_name = NULL;
838 	}
839 
840 	if (priv->absolute_icon_name != NULL) {
841 		g_free(priv->absolute_icon_name);
842 		priv->absolute_icon_name = NULL;
843 	}
844 
845 	if (priv->attention_icon_name != NULL) {
846 		g_free(priv->attention_icon_name);
847 		priv->attention_icon_name = NULL;
848 	}
849 
850 	if (priv->absolute_attention_icon_name != NULL) {
851 		g_free(priv->absolute_attention_icon_name);
852 		priv->absolute_attention_icon_name = NULL;
853 	}
854 
855 	if (priv->icon_theme_path != NULL) {
856 		g_free(priv->icon_theme_path);
857 		priv->icon_theme_path = NULL;
858 	}
859 
860 	if (priv->absolute_icon_theme_path != NULL) {
861 		g_free(priv->absolute_icon_theme_path);
862 		priv->absolute_icon_theme_path = NULL;
863 	}
864 
865 	if (priv->title != NULL) {
866 		g_free(priv->title);
867 		priv->title = NULL;
868 	}
869 
870 	if (priv->label != NULL) {
871 		g_free(priv->label);
872 		priv->label = NULL;
873 	}
874 
875 	if (priv->label_guide != NULL) {
876 		g_free(priv->label_guide);
877 		priv->label_guide = NULL;
878 	}
879 
880 	if (priv->accessible_desc != NULL) {
881 		g_free(priv->accessible_desc);
882 		priv->accessible_desc = NULL;
883 	}
884 
885 	if (priv->att_accessible_desc != NULL) {
886 		g_free(priv->att_accessible_desc);
887 		priv->att_accessible_desc = NULL;
888 	}
889 
890 	if (priv->path != NULL) {
891 		g_free(priv->path);
892 		priv->path = NULL;
893 	}
894 
895 	G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object);
896 	return;
897 }
898 
899 #define WARN_BAD_TYPE(prop, value)  g_warning("Can not work with property '%s' with value of type '%s'.", prop, G_VALUE_TYPE_NAME(value))
900 
901 /* Set some properties */
902 static void
app_indicator_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)903 app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
904 {
905         AppIndicator *self = APP_INDICATOR (object);
906         AppIndicatorPrivate *priv = self->priv;
907         GEnumValue *enum_val;
908 
909         switch (prop_id) {
910         case PROP_ID:
911           if (priv->id != NULL) {
912             g_warning ("Resetting ID value when I already had a value of: %s", priv->id);
913             break;
914           }
915 
916           priv->id = g_strdup (g_value_get_string (value));
917 
918           priv->clean_id = g_strdup(priv->id);
919           gchar * cleaner;
920           for (cleaner = priv->clean_id; *cleaner != '\0'; cleaner++) {
921             if (!g_ascii_isalnum(*cleaner)) {
922               *cleaner = '_';
923             }
924           }
925 
926           check_connect (self);
927           break;
928 
929         case PROP_CATEGORY:
930           enum_val = g_enum_get_value_by_nick ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY),
931                                                g_value_get_string (value));
932 
933           if (priv->category != enum_val->value)
934             {
935               priv->category = enum_val->value;
936             }
937 
938           break;
939 
940         case PROP_STATUS:
941           enum_val = g_enum_get_value_by_nick ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS),
942                                                g_value_get_string (value));
943 
944           app_indicator_set_status (APP_INDICATOR (object),
945                                     enum_val->value);
946           break;
947 
948         case PROP_ICON_NAME:
949           app_indicator_set_icon_full (APP_INDICATOR (object),
950                                        g_value_get_string (value),
951                                        priv->accessible_desc);
952           check_connect (self);
953           break;
954 
955         case PROP_ICON_DESC:
956           app_indicator_set_icon_full (APP_INDICATOR (object),
957                                        priv->icon_name,
958                                        g_value_get_string (value));
959           check_connect (self);
960           break;
961 
962         case PROP_ATTENTION_ICON_NAME:
963           app_indicator_set_attention_icon_full (APP_INDICATOR (object),
964                                                  g_value_get_string (value),
965                                                  priv->att_accessible_desc);
966           break;
967 
968         case PROP_ATTENTION_ICON_DESC:
969           app_indicator_set_attention_icon_full (APP_INDICATOR (object),
970                                                  priv->attention_icon_name,
971                                                  g_value_get_string (value));
972           break;
973 
974         case PROP_ICON_THEME_PATH:
975           app_indicator_set_icon_theme_path (APP_INDICATOR (object),
976                                             g_value_get_string (value));
977           check_connect (self);
978           break;
979 
980 		case PROP_LABEL: {
981 		  gchar * oldlabel = priv->label;
982 		  priv->label = g_value_dup_string(value);
983 
984 		  if (priv->label != NULL && priv->label[0] == '\0') {
985 		  	g_free(priv->label);
986 			priv->label = NULL;
987 		  }
988 
989 		  if (g_strcmp0(oldlabel, priv->label) != 0) {
990 		    signal_label_change(APP_INDICATOR(object));
991 		  }
992 
993 		  if (oldlabel != NULL) {
994 		  	g_free(oldlabel);
995 		  }
996 		  break;
997 		}
998 		case PROP_TITLE: {
999 		  gchar * oldtitle = priv->title;
1000 		  priv->title = g_value_dup_string(value);
1001 
1002 		  if (priv->title != NULL && priv->title[0] == '\0') {
1003 		  	g_free(priv->title);
1004 			priv->title = NULL;
1005 		  }
1006 
1007 		  if (g_strcmp0(oldtitle, priv->title) != 0 && self->priv->connection != NULL) {
1008 			GError * error = NULL;
1009 
1010 			g_dbus_connection_emit_signal(self->priv->connection,
1011 										  NULL,
1012 										  self->priv->path,
1013 										  NOTIFICATION_ITEM_DBUS_IFACE,
1014 										  "NewTitle",
1015 										  NULL,
1016 										  &error);
1017 
1018 			if (error != NULL) {
1019 				g_warning("Unable to send signal for NewTitle: %s", error->message);
1020 				g_error_free(error);
1021 			}
1022 		  }
1023 
1024 		  if (oldtitle != NULL) {
1025 		  	g_free(oldtitle);
1026 		  }
1027 
1028 		  if (priv->status_icon != NULL) {
1029 		  	gtk_status_icon_set_title(priv->status_icon, priv->title ? priv->title : "");
1030 		  }
1031 		  break;
1032 		}
1033 		case PROP_LABEL_GUIDE: {
1034 		  gchar * oldguide = priv->label_guide;
1035 		  priv->label_guide = g_value_dup_string(value);
1036 
1037 		  if (priv->label_guide != NULL && priv->label_guide[0] == '\0') {
1038 		  	g_free(priv->label_guide);
1039 			priv->label_guide = NULL;
1040 		  }
1041 
1042 		  if (g_strcmp0(oldguide, priv->label_guide) != 0) {
1043 		    signal_label_change(APP_INDICATOR(object));
1044 		  }
1045 
1046 		  if (priv->label_guide != NULL && priv->label_guide[0] == '\0') {
1047 		  	g_free(priv->label_guide);
1048 			priv->label_guide = NULL;
1049 		  }
1050 
1051 		  if (oldguide != NULL) {
1052 		  	g_free(oldguide);
1053 		  }
1054 		  break;
1055 		}
1056 		case PROP_ORDERING_INDEX:
1057 		  priv->ordering_index = g_value_get_uint(value);
1058 		  break;
1059 
1060 		case PROP_DBUS_MENU_SERVER:
1061 			g_clear_object (&priv->menuservice);
1062 			priv->menuservice = DBUSMENU_SERVER (g_value_dup_object(value));
1063 			break;
1064 
1065         default:
1066           G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1067           break;
1068         }
1069 
1070 	return;
1071 }
1072 
1073 /* Function to fill our value with the property it's requesting. */
1074 static void
app_indicator_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1075 app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
1076 {
1077         AppIndicator *self = APP_INDICATOR (object);
1078         AppIndicatorPrivate *priv = self->priv;
1079         GEnumValue *enum_value;
1080 
1081         switch (prop_id) {
1082         case PROP_ID:
1083           g_value_set_string (value, priv->id);
1084           break;
1085 
1086         case PROP_CATEGORY:
1087           enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), priv->category);
1088           g_value_set_string (value, enum_value->value_nick);
1089           break;
1090 
1091         case PROP_STATUS:
1092           enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), priv->status);
1093           g_value_set_string (value, enum_value->value_nick);
1094           break;
1095 
1096         case PROP_ICON_NAME:
1097           g_value_set_string (value, priv->icon_name);
1098           break;
1099 
1100         case PROP_ICON_DESC:
1101           g_value_set_string (value, priv->accessible_desc);
1102           break;
1103 
1104         case PROP_ATTENTION_ICON_NAME:
1105           g_value_set_string (value, priv->attention_icon_name);
1106           break;
1107 
1108         case PROP_ATTENTION_ICON_DESC:
1109           g_value_set_string (value, priv->att_accessible_desc);
1110           break;
1111 
1112         case PROP_ICON_THEME_PATH:
1113           g_value_set_string (value, priv->icon_theme_path);
1114           break;
1115 
1116 		case PROP_CONNECTED: {
1117 			gboolean connected = FALSE;
1118 
1119 			if (priv->watcher_proxy != NULL) {
1120 				gchar * name = g_dbus_proxy_get_name_owner(priv->watcher_proxy);
1121 				if (name != NULL) {
1122 					connected = TRUE;
1123 					g_free(name);
1124 				}
1125 			}
1126 
1127 			g_value_set_boolean (value, connected);
1128 			break;
1129 		}
1130 
1131         case PROP_LABEL:
1132           g_value_set_string (value, priv->label);
1133           break;
1134 
1135         case PROP_LABEL_GUIDE:
1136           g_value_set_string (value, priv->label_guide);
1137           break;
1138 
1139 		case PROP_ORDERING_INDEX:
1140 		  g_value_set_uint(value, priv->ordering_index);
1141 		  break;
1142 
1143 		case PROP_DBUS_MENU_SERVER:
1144 			g_value_set_object(value, priv->menuservice);
1145 			break;
1146 
1147 		case PROP_TITLE:
1148 			g_value_set_string(value, priv->title);
1149 			break;
1150 
1151         default:
1152           G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1153           break;
1154         }
1155 
1156 	return;
1157 }
1158 
1159 /* DBus bus has been created, well maybe, but we got a call
1160    back about it so we need to check into it. */
1161 static void
bus_creation(GObject * obj,GAsyncResult * res,gpointer user_data)1162 bus_creation (GObject * obj, GAsyncResult * res, gpointer user_data)
1163 {
1164 	GError * error = NULL;
1165 
1166 	GDBusConnection * connection = g_bus_get_finish(res, &error);
1167 	if (error != NULL) {
1168 		g_warning("Unable to get the session bus: %s", error->message);
1169 		g_error_free(error);
1170 		g_object_unref(G_OBJECT(user_data));
1171 		return;
1172 	}
1173 
1174 	AppIndicator * app = APP_INDICATOR(user_data);
1175 	app->priv->connection = connection;
1176 
1177 	/* If the connection was blocking the exporting of the
1178 	   object this function will export everything. */
1179 	check_connect(app);
1180 
1181 	g_object_unref(G_OBJECT(app));
1182 
1183 	return;
1184 }
1185 
1186 static void
bus_method_call(GDBusConnection * connection,const gchar * sender,const gchar * path,const gchar * interface,const gchar * method,GVariant * params,GDBusMethodInvocation * invocation,gpointer user_data)1187 bus_method_call (GDBusConnection * connection, const gchar * sender,
1188                  const gchar * path, const gchar * interface,
1189                  const gchar * method, GVariant * params,
1190                  GDBusMethodInvocation * invocation, gpointer user_data)
1191 {
1192 	g_return_if_fail(IS_APP_INDICATOR(user_data));
1193 
1194 	AppIndicator * app = APP_INDICATOR(user_data);
1195 	AppIndicatorPrivate * priv = app->priv;
1196 	GVariant * retval = NULL;
1197 
1198 	if (g_strcmp0(method, "Scroll") == 0) {
1199 		GdkScrollDirection direction;
1200 		gint delta;
1201 		const gchar *orientation;
1202 
1203 		g_variant_get(params, "(i&s)", &delta, &orientation);
1204 
1205 		if (g_strcmp0(orientation, "horizontal") == 0) {
1206 			direction = (delta >= 0) ? GDK_SCROLL_RIGHT : GDK_SCROLL_LEFT;
1207 		} else if (g_strcmp0(orientation, "vertical") == 0) {
1208 			direction = (delta >= 0) ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
1209 		} else {
1210 			g_dbus_method_invocation_return_value(invocation, retval);
1211 			return;
1212 		}
1213 
1214 		delta = ABS(delta);
1215 		g_signal_emit(app, signals[SCROLL_EVENT], 0, delta, direction);
1216 
1217 	} else if (g_strcmp0(method, "SecondaryActivate") == 0 ||
1218 	           g_strcmp0(method, "XAyatanaSecondaryActivate") == 0) {
1219 		GtkWidget *menuitem = priv->sec_activate_target;
1220 
1221 		if (priv->sec_activate_enabled && menuitem &&
1222 		    gtk_widget_get_visible (menuitem) &&
1223 		    gtk_widget_get_sensitive (menuitem))
1224 		{
1225 			gtk_widget_activate (menuitem);
1226 		}
1227 	} else {
1228 		g_warning("Calling method '%s' on the app-indicator and it's unknown", method);
1229 	}
1230 
1231 	g_dbus_method_invocation_return_value(invocation, retval);
1232 }
1233 
1234 /* DBus is asking for a property so we should figure out what it
1235    wants and try and deliver. */
1236 static GVariant *
bus_get_prop(GDBusConnection * connection,const gchar * sender,const gchar * path,const gchar * interface,const gchar * property,GError ** error,gpointer user_data)1237 bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
1238 {
1239 	g_return_val_if_fail(IS_APP_INDICATOR(user_data), NULL);
1240 	AppIndicator * app = APP_INDICATOR(user_data);
1241 	AppIndicatorPrivate *priv = app->priv;
1242 
1243 	if (g_strcmp0(property, "Id") == 0) {
1244 		return g_variant_new_string(app->priv->id ? app->priv->id : "");
1245 	} else if (g_strcmp0(property, "Category") == 0) {
1246         GEnumValue *enum_value;
1247 		enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), priv->category);
1248 		return g_variant_new_string(enum_value->value_nick ? enum_value->value_nick : "");
1249 	} else if (g_strcmp0(property, "Status") == 0) {
1250         GEnumValue *enum_value;
1251 		enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), priv->status);
1252 		return g_variant_new_string(enum_value->value_nick ? enum_value->value_nick : "");
1253 	} else if (g_strcmp0(property, "IconName") == 0) {
1254 		if (priv->absolute_icon_name) {
1255 			return g_variant_new_string(priv->absolute_icon_name);
1256 		}
1257 		return g_variant_new_string(priv->icon_name ? priv->icon_name : "");
1258 	} else if (g_strcmp0(property, "AttentionIconName") == 0) {
1259 		if (priv->absolute_attention_icon_name) {
1260 			return g_variant_new_string(priv->absolute_attention_icon_name);
1261 		}
1262 		return g_variant_new_string(priv->attention_icon_name ? priv->attention_icon_name : "");
1263 	} else if (g_strcmp0(property, "Title") == 0) {
1264 		const gchar * output = NULL;
1265 		if (priv->title == NULL) {
1266 			const gchar * name = g_get_application_name();
1267 			if (name != NULL) {
1268 				output = name;
1269 			} else {
1270 				output = "";
1271 			}
1272 		} else {
1273 			output = priv->title;
1274 		}
1275 		return g_variant_new_string(output);
1276 	} else if (g_strcmp0(property, "IconThemePath") == 0) {
1277 		if (priv->absolute_icon_theme_path) {
1278 			return g_variant_new_string(priv->absolute_icon_theme_path);
1279 		}
1280 		return g_variant_new_string(priv->icon_theme_path ? priv->icon_theme_path : "");
1281 	} else if (g_strcmp0(property, "Menu") == 0) {
1282 		if (priv->menuservice != NULL) {
1283 			GValue strval = { 0 };
1284 			g_value_init(&strval, G_TYPE_STRING);
1285 			g_object_get_property (G_OBJECT (priv->menuservice), DBUSMENU_SERVER_PROP_DBUS_OBJECT, &strval);
1286 			GVariant * var = g_variant_new("o", g_value_get_string(&strval));
1287 			g_value_unset(&strval);
1288 			return var;
1289 		} else {
1290 			return g_variant_new("o", "/");
1291 		}
1292 	} else if (g_strcmp0(property, "XAyatanaLabel") == 0) {
1293 		return g_variant_new_string(priv->label ? priv->label : "");
1294 	} else if (g_strcmp0(property, "XAyatanaLabelGuide") == 0) {
1295 		return g_variant_new_string(priv->label_guide ? priv->label_guide : "");
1296 	} else if (g_strcmp0(property, "XAyatanaOrderingIndex") == 0) {
1297 		return g_variant_new_uint32(priv->ordering_index);
1298 	} else if (g_strcmp0(property, "IconAccessibleDesc") == 0) {
1299 		return g_variant_new_string(priv->accessible_desc ? priv->accessible_desc : "");
1300 	} else if (g_strcmp0(property, "AttentionAccessibleDesc") == 0) {
1301 		return g_variant_new_string(priv->att_accessible_desc ? priv->att_accessible_desc : "");
1302 	}
1303 
1304 	*error = g_error_new(0, 0, "Unknown property: %s", property);
1305 	return NULL;
1306 }
1307 
1308 /* Sends the label changed signal and resets the source ID */
1309 static gboolean
signal_label_change_idle(gpointer user_data)1310 signal_label_change_idle (gpointer user_data)
1311 {
1312 	AppIndicator * self = (AppIndicator *)user_data;
1313 	AppIndicatorPrivate *priv = self->priv;
1314 
1315 	gchar * label = priv->label != NULL ? priv->label : "";
1316 	gchar * guide = priv->label_guide != NULL ? priv->label_guide : "";
1317 
1318 	g_signal_emit(G_OBJECT(self), signals[NEW_LABEL], 0,
1319 	              label, guide);
1320 	if (priv->dbus_registration != 0 && priv->connection != NULL) {
1321 		GError * error = NULL;
1322 
1323 		g_dbus_connection_emit_signal(priv->connection,
1324 		                              NULL,
1325 		                              priv->path,
1326 		                              NOTIFICATION_ITEM_DBUS_IFACE,
1327 		                              "XAyatanaNewLabel",
1328 		                              g_variant_new("(ss)", label, guide),
1329 		                              &error);
1330 
1331 		if (error != NULL) {
1332 			g_warning("Unable to send signal for NewIcon: %s", error->message);
1333 			g_error_free(error);
1334 		}
1335 	}
1336 
1337 	priv->label_change_idle = 0;
1338 
1339 	return FALSE;
1340 }
1341 
1342 /* Sets up an idle function to send the label changed signal
1343    so that we don't send it too many times. */
1344 static void
signal_label_change(AppIndicator * self)1345 signal_label_change (AppIndicator * self)
1346 {
1347 	AppIndicatorPrivate *priv = self->priv;
1348 
1349 	/* don't set it twice */
1350 	if (priv->label_change_idle != 0) {
1351 		return;
1352 	}
1353 
1354 	priv->label_change_idle = g_idle_add(signal_label_change_idle, self);
1355 	return;
1356 }
1357 
1358 /* This function is used to see if we have enough information to
1359    connect to things.  If we do, and we're not connected, it
1360    connects for us. */
1361 static void
check_connect(AppIndicator * self)1362 check_connect (AppIndicator *self)
1363 {
1364 	AppIndicatorPrivate *priv = self->priv;
1365 
1366 	/* Do we have a connection? */
1367 	if (priv->connection == NULL) return;
1368 
1369 	/* If we already have a proxy, let's see if it has someone
1370 	   implementing it.  If not, we can't do much more than to
1371 	   do nothing. */
1372 	if (priv->watcher_proxy != NULL) {
1373 		gchar * name = g_dbus_proxy_get_name_owner(priv->watcher_proxy);
1374 		if (name == NULL) {
1375 			return;
1376 		}
1377 		g_free(name);
1378 	}
1379 
1380 	/* Do we have enough information? */
1381 	if (priv->menu == NULL) return;
1382 	if (priv->icon_name == NULL) return;
1383 	if (priv->id == NULL) return;
1384 
1385 	if (priv->path == NULL) {
1386 		priv->path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s", priv->clean_id);
1387 	}
1388 
1389 	if (priv->dbus_registration == 0) {
1390 		GError * error = NULL;
1391 		priv->dbus_registration = g_dbus_connection_register_object(priv->connection,
1392 		                                                            priv->path,
1393 		                                                            item_interface_info,
1394 		                                                            &item_interface_table,
1395 		                                                            self,
1396 		                                                            NULL,
1397 		                                                            &error);
1398 		if (error != NULL) {
1399 			g_warning("Unable to register object on path '%s': %s", priv->path, error->message);
1400 			g_error_free(error);
1401 			return;
1402 		}
1403 	}
1404 
1405 	/* NOTE: It's really important the order here.  We make sure to *publish*
1406 	   the object on the bus and *then* get the proxy.  The reason is that we
1407 	   want to ensure all the filters are setup before talking to the watcher
1408 	   and that's where the order is important. */
1409 
1410 	if (priv->watcher_proxy == NULL)
1411 		return;
1412 
1413 	g_dbus_proxy_call (priv->watcher_proxy,
1414 	                   "RegisterStatusNotifierItem",
1415 	                   g_variant_new ("(s)", priv->path),
1416 	                   G_DBUS_CALL_FLAGS_NONE,
1417 	                   -1, NULL,
1418 	                   (GAsyncReadyCallback) register_service_cb,
1419 	                   g_object_ref (self));
1420 }
1421 
1422 /* Responce from the DBus command to register a service
1423    with a NotificationWatcher. */
1424 static void
register_service_cb(GObject * obj,GAsyncResult * res,gpointer user_data)1425 register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
1426 {
1427 	GError * error = NULL;
1428 	GVariant * returns = g_dbus_proxy_call_finish(G_DBUS_PROXY(obj), res, &error);
1429 
1430 	/* We don't care about any return values */
1431 	if (returns != NULL) {
1432 		g_variant_unref(returns);
1433 	}
1434 
1435 	if (error != NULL) {
1436 		/* They didn't respond, ewww.  Not sure what they could
1437 		   be doing */
1438 		g_warning("Unable to connect to the Notification Watcher: %s", error->message);
1439 		start_fallback_timer(APP_INDICATOR(user_data), TRUE);
1440 		g_object_unref(G_OBJECT(user_data));
1441 		return;
1442 	}
1443 
1444 	g_return_if_fail(IS_APP_INDICATOR(user_data));
1445 	AppIndicator * app = APP_INDICATOR(user_data);
1446 	AppIndicatorPrivate * priv = app->priv;
1447 
1448 	/* Emit the AppIndicator::connection-changed signal*/
1449     g_signal_emit (app, signals[CONNECTION_CHANGED], 0, TRUE);
1450 
1451 	if (priv->status_icon) {
1452 		AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(app);
1453 		if (class->unfallback != NULL) {
1454 			class->unfallback(app, priv->status_icon);
1455 			priv->status_icon = NULL;
1456 		}
1457 	}
1458 
1459 	g_object_unref(G_OBJECT(user_data));
1460 	return;
1461 }
1462 
1463 /* A helper function to get the nick out of a given
1464    category enum value. */
1465 static const gchar *
category_from_enum(AppIndicatorCategory category)1466 category_from_enum (AppIndicatorCategory category)
1467 {
1468   GEnumValue *value;
1469 
1470   value = g_enum_get_value ((GEnumClass *)g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), category);
1471   return value->value_nick;
1472 }
1473 
1474 /* A function that will start the fallback timer if it's not
1475    already started.  It sets up the DBus watcher to see if
1476    there is a change.  Also, provides an override mode for cases
1477    where it's unlikely that a timer will help anything. */
1478 static void
start_fallback_timer(AppIndicator * self,gboolean disable_timeout)1479 start_fallback_timer (AppIndicator * self, gboolean disable_timeout)
1480 {
1481 	g_return_if_fail(IS_APP_INDICATOR(self));
1482 	AppIndicatorPrivate * priv = APP_INDICATOR(self)->priv;
1483 
1484 	if (priv->fallback_timer != 0) {
1485 		/* The timer is set, let's just be happy with the one
1486 		   we've already got running */
1487 		return;
1488 	}
1489 
1490 	if (priv->status_icon != NULL) {
1491 		/* We're already fallen back.  Let's not do it again. */
1492 		return;
1493 	}
1494 
1495 	if (disable_timeout) {
1496 		fallback_timer_expire(self);
1497 	} else {
1498 		priv->fallback_timer = g_timeout_add(DEFAULT_FALLBACK_TIMER, fallback_timer_expire, self);
1499 	}
1500 
1501 	return;
1502 }
1503 
1504 /* A function that gets executed when we want to change the
1505    state of the fallback. */
1506 static gboolean
fallback_timer_expire(gpointer data)1507 fallback_timer_expire (gpointer data)
1508 {
1509 	g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
1510 
1511 	AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
1512 	AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data);
1513 
1514 	if (priv->status_icon == NULL) {
1515 		if (class->fallback != NULL) {
1516 			priv->status_icon = class->fallback(APP_INDICATOR(data));
1517 		}
1518 	} else {
1519 		if (class->unfallback != NULL) {
1520 			class->unfallback(APP_INDICATOR(data), priv->status_icon);
1521 			priv->status_icon = NULL;
1522 		} else {
1523 			g_warning("No 'unfallback' function but the 'fallback' function returned a non-NULL result.");
1524 		}
1525 	}
1526 
1527 	priv->fallback_timer = 0;
1528 	return FALSE;
1529 }
1530 
1531 /* emit a NEW_ICON signal in response for the theme change */
1532 static void
theme_changed_cb(GtkIconTheme * theme,gpointer user_data)1533 theme_changed_cb (GtkIconTheme * theme, gpointer user_data)
1534 {
1535 	g_signal_emit (user_data, signals[NEW_ICON], 0);
1536 
1537 	AppIndicator * self = (AppIndicator *)user_data;
1538 	AppIndicatorPrivate *priv = self->priv;
1539 
1540 	if (priv->dbus_registration != 0 && priv->connection != NULL) {
1541 		GError * error = NULL;
1542 
1543 		g_dbus_connection_emit_signal(priv->connection,
1544 		                              NULL,
1545 		                              priv->path,
1546 		                              NOTIFICATION_ITEM_DBUS_IFACE,
1547 		                              "NewIcon",
1548 		                              NULL,
1549 		                              &error);
1550 
1551 		if (error != NULL) {
1552 			g_warning("Unable to send signal for NewIcon: %s", error->message);
1553 			g_error_free(error);
1554 		}
1555 	}
1556 
1557 	return;
1558 }
1559 
1560 /* Creates a StatusIcon that can be used when the application
1561    indicator area isn't available. */
1562 static GtkStatusIcon *
fallback(AppIndicator * self)1563 fallback (AppIndicator * self)
1564 {
1565 	GtkStatusIcon * icon = gtk_status_icon_new();
1566 
1567 	gtk_status_icon_set_name(icon, app_indicator_get_id(self));
1568 	const gchar * title = app_indicator_get_title(self);
1569 	if (title != NULL) {
1570 		gtk_status_icon_set_title(icon, title);
1571 	}
1572 
1573 	g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_STATUS,
1574 		G_CALLBACK(status_icon_status_wrapper), icon);
1575 	g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ICON,
1576 		G_CALLBACK(status_icon_changes), icon);
1577 	g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON,
1578 		G_CALLBACK(status_icon_changes), icon);
1579 
1580 	status_icon_changes(self, icon);
1581 
1582 	g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self);
1583 	g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(status_icon_menu_activate), self);
1584 	g_signal_connect(G_OBJECT(icon), "scroll-event", G_CALLBACK(scroll_event_wrapper), self);
1585 	g_signal_connect(G_OBJECT(icon), "button-release-event", G_CALLBACK(middle_click_wrapper), self);
1586 
1587 	return icon;
1588 }
1589 
1590 /* A wrapper as the status update prototype is a little
1591    bit different, but we want to handle it the same. */
1592 static void
status_icon_status_wrapper(AppIndicator * self,const gchar * status,gpointer data)1593 status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data)
1594 {
1595 	return status_icon_changes(self, data);
1596 }
1597 
1598 /* A wrapper for redirecting the scroll events to the app-indicator from status
1599    icon widget. */
1600 static gboolean
scroll_event_wrapper(GtkWidget * status_icon,GdkEventScroll * event,gpointer data)1601 scroll_event_wrapper (GtkWidget *status_icon, GdkEventScroll *event, gpointer data)
1602 {
1603 	g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
1604 	AppIndicator * app = APP_INDICATOR(data);
1605 	g_signal_emit(app, signals[SCROLL_EVENT], 0, 1, event->direction);
1606 
1607 	return TRUE;
1608 }
1609 
1610 static gboolean
middle_click_wrapper(GtkWidget * status_icon,GdkEventButton * event,gpointer data)1611 middle_click_wrapper (GtkWidget *status_icon, GdkEventButton *event, gpointer data)
1612 {
1613 	g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
1614 	AppIndicator * app = APP_INDICATOR(data);
1615 	AppIndicatorPrivate *priv = app->priv;
1616 
1617 	if (event->button == 2 && event->type == GDK_BUTTON_RELEASE) {
1618 		GtkAllocation alloc;
1619 		gint px = event->x;
1620 		gint py = event->y;
1621 		gtk_widget_get_allocation (status_icon, &alloc);
1622 		GtkWidget *menuitem = priv->sec_activate_target;
1623 
1624 		if (px >= 0 && px < alloc.width && py >= 0 && py < alloc.height &&
1625 		    priv->sec_activate_enabled && menuitem &&
1626 		    gtk_widget_get_visible (menuitem) &&
1627 		    gtk_widget_get_sensitive (menuitem))
1628 		{
1629 			gtk_widget_activate (menuitem);
1630 			return TRUE;
1631 		}
1632 	}
1633 
1634 	return FALSE;
1635 }
1636 
1637 /* This tracks changes to either the status or the icons
1638    that are associated with the app indicator */
1639 static void
status_icon_changes(AppIndicator * self,gpointer data)1640 status_icon_changes (AppIndicator * self, gpointer data)
1641 {
1642 	GtkStatusIcon * icon = GTK_STATUS_ICON(data);
1643 
1644 	/* add the icon_theme_path once if needed */
1645 	GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
1646 	const gchar *theme_path = self->priv->absolute_icon_theme_path ?
1647 	                            self->priv->absolute_icon_theme_path :
1648 	                            self->priv->icon_theme_path;
1649 
1650 	if (theme_path != NULL) {
1651 		gchar **path;
1652 		gint n_elements, i;
1653 		gboolean found=FALSE;
1654 		gtk_icon_theme_get_search_path(icon_theme, &path, &n_elements);
1655 		for (i=0; i< n_elements; i++) {
1656 			if(g_strcmp0(path[i], theme_path) == 0) {
1657 				found=TRUE;
1658 				break;
1659 			}
1660 		}
1661 		if(!found) {
1662 			gtk_icon_theme_append_search_path(icon_theme, theme_path);
1663 		}
1664 		g_strfreev (path);
1665 	}
1666 
1667 	const gchar * icon_name = NULL;
1668 	switch (app_indicator_get_status(self)) {
1669 	case APP_INDICATOR_STATUS_PASSIVE:
1670 		/* hide first to avoid that the change is visible to the user */
1671 		gtk_status_icon_set_visible(icon, FALSE);
1672 		icon_name = app_indicator_get_icon(self);
1673 		break;
1674 	case APP_INDICATOR_STATUS_ACTIVE:
1675 		icon_name = app_indicator_get_icon(self);
1676 		gtk_status_icon_set_visible(icon, TRUE);
1677 		break;
1678 	case APP_INDICATOR_STATUS_ATTENTION:
1679 		/* get the _attention_ icon here */
1680 		icon_name = app_indicator_get_attention_icon(self);
1681 		gtk_status_icon_set_visible(icon, TRUE);
1682 		break;
1683 	};
1684 
1685 	if (icon_name != NULL) {
1686 		gchar *snapped_icon = append_snap_prefix(icon_name);
1687 
1688 		if (g_file_test(icon_name, G_FILE_TEST_EXISTS)) {
1689 			gtk_status_icon_set_from_file(icon, icon_name);
1690 		} else if (snapped_icon && g_file_test(snapped_icon, G_FILE_TEST_EXISTS)) {
1691 			gtk_status_icon_set_from_file(icon, snapped_icon);
1692 		} else {
1693 			gchar *longname = append_panel_icon_suffix(icon_name);
1694 
1695 			if (longname != NULL && gtk_icon_theme_has_icon (icon_theme, longname)) {
1696 				gtk_status_icon_set_from_icon_name(icon, longname);
1697 			} else {
1698 				gtk_status_icon_set_from_icon_name(icon, icon_name);
1699 			}
1700 
1701 			g_free(longname);
1702 		}
1703 
1704 		g_free(snapped_icon);
1705 	}
1706 
1707 	return;
1708 }
1709 
1710 /* Handles the activate action by the status icon by showing
1711    the menu in a popup. */
1712 static void
status_icon_activate(GtkStatusIcon * icon,gpointer data)1713 status_icon_activate (GtkStatusIcon * icon, gpointer data)
1714 {
1715 	GtkMenu * menu = app_indicator_get_menu(APP_INDICATOR(data));
1716 	if (menu == NULL)
1717 		return;
1718 
1719 	gtk_menu_popup(menu,
1720 	               NULL, /* Parent Menu */
1721 	               NULL, /* Parent item */
1722 	               gtk_status_icon_position_menu,
1723 	               icon,
1724 	               1, /* Button */
1725 	               gtk_get_current_event_time());
1726 
1727 	return;
1728 }
1729 
1730 /* Handles the right-click action by the status icon by showing
1731    the menu in a popup. */
1732 static void
status_icon_menu_activate(GtkStatusIcon * status_icon,guint button,guint activate_time,gpointer user_data)1733 status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
1734 {
1735 	status_icon_activate(status_icon, user_data);
1736 }
1737 
1738 /* Removes the status icon as the application indicator area
1739    is now up and running again. */
1740 static void
unfallback(AppIndicator * self,GtkStatusIcon * status_icon)1741 unfallback (AppIndicator * self, GtkStatusIcon * status_icon)
1742 {
1743 	g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_status_wrapper, status_icon);
1744 	g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_changes, status_icon);
1745 	g_signal_handlers_disconnect_by_func(G_OBJECT(self), scroll_event_wrapper, status_icon);
1746 	g_signal_handlers_disconnect_by_func(G_OBJECT(self), middle_click_wrapper, status_icon);
1747 	gtk_status_icon_set_visible(status_icon, FALSE);
1748 	g_object_unref(G_OBJECT(status_icon));
1749 	return;
1750 }
1751 
1752 /* A helper function that appends PANEL_ICON_SUFFIX to the given icon name
1753    if it's missing. */
1754 static gchar *
append_panel_icon_suffix(const gchar * icon_name)1755 append_panel_icon_suffix (const gchar *icon_name)
1756 {
1757 	gchar * long_name = NULL;
1758 
1759 	if (!g_str_has_suffix (icon_name, PANEL_ICON_SUFFIX)) {
1760 		long_name =
1761 		    g_strdup_printf("%s-%s", icon_name, PANEL_ICON_SUFFIX);
1762         } else {
1763            	long_name = g_strdup (icon_name);
1764         }
1765 
1766 	return long_name;
1767 }
1768 
1769 static gboolean
widget_is_menu_child(AppIndicator * self,GtkWidget * child)1770 widget_is_menu_child(AppIndicator * self, GtkWidget *child)
1771 {
1772 	g_return_val_if_fail(IS_APP_INDICATOR(self), FALSE);
1773 
1774 	if (!self->priv->menu) return FALSE;
1775 	if (!child) return FALSE;
1776 
1777 	GtkWidget *parent;
1778 
1779 	while ((parent = gtk_widget_get_parent(child))) {
1780 		if (parent == self->priv->menu)
1781 			return TRUE;
1782 
1783 		if (GTK_IS_MENU(parent))
1784 			child = gtk_menu_get_attach_widget(GTK_MENU(parent));
1785 		else
1786 			child = parent;
1787 	}
1788 
1789 	return FALSE;
1790 }
1791 
1792 static void
sec_activate_target_parent_changed(GtkWidget * menuitem,GtkWidget * old_parent,gpointer data)1793 sec_activate_target_parent_changed(GtkWidget *menuitem, GtkWidget *old_parent,
1794                                    gpointer data)
1795 {
1796 	g_return_if_fail(IS_APP_INDICATOR(data));
1797 	AppIndicator *self = data;
1798 	self->priv->sec_activate_enabled = widget_is_menu_child(self, menuitem);
1799 }
1800 
1801 
1802 /* ************************* */
1803 /*    Public Functions       */
1804 /* ************************* */
1805 
1806 /**
1807  * app_indicator_new:
1808  * @id: The unique id of the indicator to create.
1809  * @icon_name: The icon name for this indicator
1810  * @category: The category of indicator.
1811  *
1812  * Creates a new #AppIndicator setting the properties:
1813  * #AppIndicator:id with @id, #AppIndicator:category with @category
1814  * and #AppIndicator:icon-name with @icon_name.
1815  *
1816  * Return value: A pointer to a new #AppIndicator object.
1817  */
1818 AppIndicator *
app_indicator_new(const gchar * id,const gchar * icon_name,AppIndicatorCategory category)1819 app_indicator_new (const gchar          *id,
1820                    const gchar          *icon_name,
1821                    AppIndicatorCategory  category)
1822 {
1823   AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
1824                                           PROP_ID_S, id,
1825                                           PROP_CATEGORY_S, category_from_enum (category),
1826                                           PROP_ICON_NAME_S, icon_name,
1827                                           NULL);
1828 
1829   return indicator;
1830 }
1831 
1832 /**
1833  * app_indicator_new_with_path:
1834  * @id: The unique id of the indicator to create.
1835  * @icon_name: The icon name for this indicator
1836  * @category: The category of indicator.
1837  * @icon_theme_path: A custom path for finding icons.
1838 
1839  * Creates a new #AppIndicator setting the properties:
1840  * #AppIndicator:id with @id, #AppIndicator:category with @category,
1841  * #AppIndicator:icon-name with @icon_name and #AppIndicator:icon-theme-path
1842  * with @icon_theme_path.
1843  *
1844  * Return value: A pointer to a new #AppIndicator object.
1845  */
1846 AppIndicator *
app_indicator_new_with_path(const gchar * id,const gchar * icon_name,AppIndicatorCategory category,const gchar * icon_theme_path)1847 app_indicator_new_with_path (const gchar          *id,
1848                              const gchar          *icon_name,
1849                              AppIndicatorCategory  category,
1850                              const gchar          *icon_theme_path)
1851 {
1852 	AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
1853 	                                        PROP_ID_S, id,
1854 	                                        PROP_CATEGORY_S, category_from_enum (category),
1855 	                                        PROP_ICON_NAME_S, icon_name,
1856 	                                        PROP_ICON_THEME_PATH_S, icon_theme_path,
1857 	                                        NULL);
1858 
1859 	return indicator;
1860 }
1861 
1862 /**
1863  * app_indicator_get_type:
1864  *
1865  * Generates or returns the unique #GType for #AppIndicator.
1866  *
1867  * Return value: A unique #GType for #AppIndicator objects.
1868  */
1869 
1870 /**
1871  * app_indicator_set_status:
1872  * @self: The #AppIndicator object to use
1873  * @status: The status to set for this indicator
1874  *
1875  * Wrapper function for property #AppIndicator:status.
1876  */
1877 void
app_indicator_set_status(AppIndicator * self,AppIndicatorStatus status)1878 app_indicator_set_status (AppIndicator *self, AppIndicatorStatus status)
1879 {
1880 	g_return_if_fail (IS_APP_INDICATOR (self));
1881 
1882 	if (self->priv->status != status) {
1883 		GEnumValue *value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), status);
1884 
1885 		self->priv->status = status;
1886 		g_signal_emit (self, signals[NEW_STATUS], 0, value->value_nick);
1887 
1888 		if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
1889 			GError * error = NULL;
1890 
1891 			g_dbus_connection_emit_signal(self->priv->connection,
1892 										  NULL,
1893 										  self->priv->path,
1894 										  NOTIFICATION_ITEM_DBUS_IFACE,
1895 										  "NewStatus",
1896 										  g_variant_new("(s)", value->value_nick),
1897 										  &error);
1898 
1899 			if (error != NULL) {
1900 				g_warning("Unable to send signal for NewStatus: %s", error->message);
1901 				g_error_free(error);
1902 			}
1903 		}
1904 	}
1905 
1906 	return;
1907 }
1908 
1909 /**
1910  * app_indicator_set_attention_icon:
1911  * @self: The #AppIndicator object to use
1912  * @icon_name: The name of the attention icon to set for this indicator
1913  *
1914  * Wrapper for app_indicator_set_attention_icon_full() with a NULL
1915  * description.
1916  *
1917  * Deprecated: Use app_indicator_set_attention_icon_full() instead.
1918  */
1919 void
app_indicator_set_attention_icon(AppIndicator * self,const gchar * icon_name)1920 app_indicator_set_attention_icon (AppIndicator *self, const gchar *icon_name)
1921 {
1922 	return app_indicator_set_attention_icon_full(self, icon_name, NULL);
1923 }
1924 
1925 /**
1926  * app_indicator_set_attention_icon_full:
1927  * @self: The #AppIndicator object to use
1928  * @icon_name: The name of the attention icon to set for this indicator
1929  * @icon_desc: A textual description of the icon
1930  *
1931  * Wrapper function for property #AppIndicator:attention-icon-name.
1932  */
1933 void
app_indicator_set_attention_icon_full(AppIndicator * self,const gchar * icon_name,const gchar * icon_desc)1934 app_indicator_set_attention_icon_full (AppIndicator *self, const gchar *icon_name, const gchar * icon_desc)
1935 {
1936 	g_return_if_fail (IS_APP_INDICATOR (self));
1937 	g_return_if_fail (icon_name != NULL);
1938 	gboolean changed = FALSE;
1939 
1940 	if (g_strcmp0 (self->priv->attention_icon_name, icon_name) != 0) {
1941 		g_free (self->priv->attention_icon_name);
1942 		self->priv->attention_icon_name = g_strdup (icon_name);
1943 
1944 		g_free(self->priv->absolute_attention_icon_name);
1945 		self->priv->absolute_attention_icon_name = NULL;
1946 
1947 		if (icon_name && icon_name[0] == '/') {
1948 			self->priv->absolute_attention_icon_name = append_snap_prefix (icon_name);
1949 		}
1950 
1951 		changed = TRUE;
1952 	}
1953 
1954 	if (g_strcmp0(self->priv->att_accessible_desc, icon_desc) != 0) {
1955 		g_free (self->priv->att_accessible_desc);
1956 		self->priv->att_accessible_desc = g_strdup (icon_desc);
1957 		changed = TRUE;
1958 	}
1959 
1960 	if (changed) {
1961 		g_signal_emit (self, signals[NEW_ATTENTION_ICON], 0);
1962 
1963 		if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
1964 			GError * error = NULL;
1965 
1966 			g_dbus_connection_emit_signal(self->priv->connection,
1967 			                              NULL,
1968 			                              self->priv->path,
1969 			                              NOTIFICATION_ITEM_DBUS_IFACE,
1970 			                              "NewAttentionIcon",
1971 			                              NULL,
1972 			                              &error);
1973 
1974 			if (error != NULL) {
1975 				g_warning("Unable to send signal for NewAttentionIcon: %s", error->message);
1976 				g_error_free(error);
1977 			}
1978 		}
1979 	}
1980 
1981 	return;
1982 }
1983 
1984 /**
1985  * app_indicator_set_icon:
1986  * @self: The #AppIndicator object to use
1987  * @icon_name: The icon name to set.
1988  *
1989  * Wrapper function for app_indicator_set_icon_full() with a NULL
1990  * description.
1991  *
1992  * Deprecated: Use app_indicator_set_icon_full()
1993  */
1994 void
app_indicator_set_icon(AppIndicator * self,const gchar * icon_name)1995 app_indicator_set_icon (AppIndicator *self, const gchar *icon_name)
1996 {
1997 	return app_indicator_set_icon_full(self, icon_name, NULL);
1998 }
1999 
2000 /**
2001  * app_indicator_set_icon_full:
2002  * @self: The #AppIndicator object to use
2003  * @icon_name: The icon name to set.
2004  * @icon_desc: A textual description of the icon for accessibility
2005  *
2006  * Sets the default icon to use when the status is active but
2007  * not set to attention.  In most cases, this should be the
2008  * application icon for the program.
2009  *
2010  * Wrapper function for property #AppIndicator:icon-name and
2011  * #AppIndicator::icon-desc.
2012  */
2013 void
app_indicator_set_icon_full(AppIndicator * self,const gchar * icon_name,const gchar * icon_desc)2014 app_indicator_set_icon_full (AppIndicator *self, const gchar *icon_name, const gchar * icon_desc)
2015 {
2016 	g_return_if_fail (IS_APP_INDICATOR (self));
2017 	g_return_if_fail (icon_name != NULL);
2018 	gboolean changed = FALSE;
2019 
2020 	if (g_strcmp0 (self->priv->icon_name, icon_name) != 0) {
2021 		if (self->priv->icon_name) {
2022 			g_free (self->priv->icon_name);
2023 		}
2024 
2025 		self->priv->icon_name = g_strdup(icon_name);
2026 
2027 		g_free(self->priv->absolute_icon_name);
2028 		self->priv->absolute_icon_name = NULL;
2029 
2030 		if (icon_name && icon_name[0] == '/') {
2031 			self->priv->absolute_icon_name = append_snap_prefix (icon_name);
2032 		}
2033 
2034 		changed = TRUE;
2035 	}
2036 
2037 	if (g_strcmp0(self->priv->accessible_desc, icon_desc) != 0) {
2038 		if (self->priv->accessible_desc != NULL) {
2039 			g_free(self->priv->accessible_desc);
2040 		}
2041 
2042 		self->priv->accessible_desc = g_strdup(icon_desc);
2043 		changed = TRUE;
2044 	}
2045 
2046 	if (changed) {
2047 		g_signal_emit (self, signals[NEW_ICON], 0);
2048 
2049 		if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
2050 			GError * error = NULL;
2051 
2052 			g_dbus_connection_emit_signal(self->priv->connection,
2053 										  NULL,
2054 										  self->priv->path,
2055 										  NOTIFICATION_ITEM_DBUS_IFACE,
2056 										  "NewIcon",
2057 										  NULL,
2058 										  &error);
2059 
2060 			if (error != NULL) {
2061 				g_warning("Unable to send signal for NewIcon: %s", error->message);
2062 				g_error_free(error);
2063 			}
2064 		}
2065 	}
2066 
2067 	return;
2068 }
2069 
2070 /**
2071  * app_indicator_set_label:
2072  * @self: The #AppIndicator object to use
2073  * @label: The label to show next to the icon.
2074  * @guide: A guide to size the label correctly.
2075  *
2076  * This is a wrapper function for the #AppIndicator:label and
2077  * #AppIndicator:guide properties.  This function can take #NULL
2078  * as either @label or @guide and will clear the entries.
2079 */
2080 void
app_indicator_set_label(AppIndicator * self,const gchar * label,const gchar * guide)2081 app_indicator_set_label (AppIndicator *self, const gchar * label, const gchar * guide)
2082 {
2083 	g_return_if_fail (IS_APP_INDICATOR (self));
2084 	/* Note: The label can be NULL, it's okay */
2085 	/* Note: The guide can be NULL, it's okay */
2086 
2087 	g_object_set(G_OBJECT(self),
2088 	             PROP_LABEL_S,       label == NULL ? "" : label,
2089 	             PROP_LABEL_GUIDE_S, guide == NULL ? "" : guide,
2090 	             NULL);
2091 
2092 	return;
2093 }
2094 
2095 static const gchar *
get_snap_prefix()2096 get_snap_prefix ()
2097 {
2098 	const gchar *snap = g_getenv ("SNAP");
2099 	return (snap && *snap != '\0') ? snap : NULL;
2100 }
2101 
2102 static gchar *
append_snap_prefix(const gchar * path)2103 append_snap_prefix (const gchar *path)
2104 {
2105 	gint i;
2106 	gchar real_path[PATH_MAX];
2107 	const gchar *snap = get_snap_prefix ();
2108 
2109 	if (snap != NULL && path != NULL) {
2110 		if (realpath (path, real_path) != NULL) {
2111 			path = real_path;
2112 		}
2113 
2114 		if (g_str_has_prefix (path, "/tmp/")) {
2115 			g_warning ("Using '/tmp' paths in SNAP environment will lead to unreadable resources");
2116 			return NULL;
2117 		}
2118 
2119 		if (g_str_has_prefix (path, snap) ||
2120 			g_str_has_prefix (path, g_get_home_dir ()) ||
2121 			g_str_has_prefix (path, g_get_user_cache_dir ()) ||
2122 			g_str_has_prefix (path, g_get_user_config_dir ()) ||
2123 			g_str_has_prefix (path, g_get_user_data_dir ()) ||
2124 			g_str_has_prefix (path, g_get_user_runtime_dir ())) {
2125 			return g_strdup (path);
2126 		}
2127 
2128 		for (i = 0; i < G_USER_N_DIRECTORIES; ++ i) {
2129 			if (g_str_has_prefix (path, g_get_user_special_dir (i))) {
2130 				return g_strdup (path);
2131 			}
2132 		}
2133 
2134 		return g_build_path (G_DIR_SEPARATOR_S, snap, path, NULL);
2135 	}
2136 
2137 	return NULL;
2138 }
2139 
2140 static gchar *
get_real_theme_path(AppIndicator * self)2141 get_real_theme_path (AppIndicator * self)
2142 {
2143 	const gchar *theme_path = self->priv->icon_theme_path;
2144 	gchar *snapped_path = append_snap_prefix (theme_path);
2145 
2146 	if (snapped_path != NULL) {
2147 		return snapped_path;
2148 	} else if (get_snap_prefix ()) {
2149 		return g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), "icons", NULL);
2150 	}
2151 
2152 	return NULL;
2153 }
2154 
2155 /**
2156  * app_indicator_set_icon_theme_path:
2157  * @self: The #AppIndicator object to use
2158  * @icon_theme_path: The icon theme path to set.
2159  *
2160  * Sets the path to use when searching for icons.
2161  */
2162 void
app_indicator_set_icon_theme_path(AppIndicator * self,const gchar * icon_theme_path)2163 app_indicator_set_icon_theme_path (AppIndicator *self, const gchar *icon_theme_path)
2164 {
2165 	g_return_if_fail (IS_APP_INDICATOR (self));
2166 
2167 	if (g_strcmp0 (self->priv->icon_theme_path, icon_theme_path) != 0) {
2168 		if (self->priv->icon_theme_path != NULL)
2169 			g_free(self->priv->icon_theme_path);
2170 
2171 		self->priv->icon_theme_path = g_strdup(icon_theme_path);
2172 
2173 		g_free (self->priv->absolute_icon_theme_path);
2174 		self->priv->absolute_icon_theme_path = get_real_theme_path (self);
2175 
2176 		g_signal_emit (self, signals[NEW_ICON_THEME_PATH], 0, self->priv->icon_theme_path);
2177 
2178 		if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
2179 			const gchar *theme_path = self->priv->absolute_icon_theme_path ?
2180 										self->priv->absolute_icon_theme_path :
2181 										self->priv->icon_theme_path;
2182 			GError * error = NULL;
2183 
2184 			g_dbus_connection_emit_signal(self->priv->connection,
2185 										  NULL,
2186 										  self->priv->path,
2187 										  NOTIFICATION_ITEM_DBUS_IFACE,
2188 										  "NewIconThemePath",
2189 										  g_variant_new("(s)", theme_path ? theme_path : ""),
2190 										  &error);
2191 
2192 			if (error != NULL) {
2193 				g_warning("Unable to send signal for NewIconThemePath: %s", error->message);
2194 				g_error_free(error);
2195 			}
2196 		}
2197 	}
2198 
2199 	return;
2200 }
2201 
2202 /* Does the dbusmenu related work.  If there isn't a server, it builds
2203    one and if there are menus it runs the parse to put those menus into
2204    the server. */
2205 static void
setup_dbusmenu(AppIndicator * self)2206 setup_dbusmenu (AppIndicator *self)
2207 {
2208 	AppIndicatorPrivate *priv;
2209 	DbusmenuMenuitem *root = NULL;
2210 
2211 	priv = self->priv;
2212 
2213 	if (priv->menu) {
2214 		root = dbusmenu_gtk_parse_menu_structure(priv->menu);
2215 	}
2216 
2217 	if (priv->menuservice == NULL) {
2218 		gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s/Menu", priv->clean_id);
2219 		priv->menuservice = dbusmenu_server_new (path);
2220 		g_free(path);
2221 	}
2222 
2223 	dbusmenu_server_set_root (priv->menuservice, root);
2224 
2225 	/* Drop our local ref as set_root should get it's own. */
2226 	if (root != NULL) {
2227 		g_object_unref(root);
2228 	}
2229 
2230 	return;
2231 }
2232 
2233 /**
2234  * app_indicator_set_menu:
2235  * @self: The #AppIndicator
2236  * @menu: (allow-none): A #GtkMenu to set
2237  *
2238  * Sets the menu that should be shown when the Application Indicator
2239  * is clicked on in the panel.  An application indicator will not
2240  * be rendered unless it has a menu.
2241  *
2242  * Wrapper function for property #AppIndicator:menu.
2243  */
2244 void
app_indicator_set_menu(AppIndicator * self,GtkMenu * menu)2245 app_indicator_set_menu (AppIndicator *self, GtkMenu *menu)
2246 {
2247   AppIndicatorPrivate *priv;
2248 
2249   g_return_if_fail (IS_APP_INDICATOR (self));
2250   g_return_if_fail (GTK_IS_MENU (menu));
2251   g_return_if_fail (self->priv->clean_id != NULL);
2252 
2253   priv = self->priv;
2254 
2255   if (priv->menu != NULL)
2256     {
2257       g_object_unref (priv->menu);
2258     }
2259 
2260   priv->menu = GTK_WIDGET (menu);
2261   g_object_ref_sink (priv->menu);
2262 
2263   setup_dbusmenu (self);
2264 
2265   priv->sec_activate_enabled = widget_is_menu_child (self, priv->sec_activate_target);
2266 
2267   check_connect (self);
2268 
2269   return;
2270 }
2271 
2272 /**
2273  * app_indicator_set_ordering_index:
2274  * @self: The #AppIndicator
2275  * @ordering_index: A value for the ordering of this app indicator
2276  *
2277  * Sets the ordering index for the app indicator which effects the
2278  * placement of it on the panel.  For almost all app indicator
2279  * this is not the function you're looking for.
2280  *
2281  * Wrapper function for property #AppIndicator:ordering-index.
2282  */
2283 void
app_indicator_set_ordering_index(AppIndicator * self,guint32 ordering_index)2284 app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index)
2285 {
2286 	g_return_if_fail (IS_APP_INDICATOR (self));
2287 
2288 	self->priv->ordering_index = ordering_index;
2289 
2290 	return;
2291 }
2292 
2293 /**
2294  * app_indicator_set_secondary_activate_target:
2295  * @self: The #AppIndicator
2296  * @menuitem: (allow-none): A #GtkWidget to be activated on secondary activation
2297  *
2298  * Set the @menuitem to be activated when a secondary activation event (i.e. a
2299  * middle-click) is emitted over the #AppIndicator icon/label.
2300  *
2301  * The @menuitem can be also a complex #GtkWidget, but to get activated when
2302  * a secondary activation occurs in the #Appindicator, it must be a visible and
2303  * active child (or inner-child) of the #AppIndicator:menu.
2304  *
2305  * Setting @menuitem to %NULL causes to disable this feature.
2306  */
2307 void
app_indicator_set_secondary_activate_target(AppIndicator * self,GtkWidget * menuitem)2308 app_indicator_set_secondary_activate_target (AppIndicator *self, GtkWidget *menuitem)
2309 {
2310 	g_return_if_fail (IS_APP_INDICATOR (self));
2311 	AppIndicatorPrivate *priv = self->priv;
2312 
2313 	if (priv->sec_activate_target) {
2314 		g_signal_handlers_disconnect_by_func (priv->sec_activate_target,
2315 		                                      sec_activate_target_parent_changed,
2316 		                                      self);
2317 		g_object_unref(G_OBJECT(priv->sec_activate_target));
2318 		priv->sec_activate_target = NULL;
2319 	}
2320 
2321 	if (menuitem == NULL) {
2322 		return;
2323 	}
2324 
2325 	g_return_if_fail (GTK_IS_WIDGET (menuitem));
2326 
2327 	priv->sec_activate_target = g_object_ref(menuitem);
2328 	priv->sec_activate_enabled = widget_is_menu_child(self, menuitem);
2329 	g_signal_connect(menuitem, "parent-set", G_CALLBACK(sec_activate_target_parent_changed), self);
2330 }
2331 
2332 /**
2333  * app_indicator_set_title:
2334  * @self: The #AppIndicator
2335  * @title: (allow-none): Title of the app indicator
2336  *
2337  * Sets the title of the application indicator, or how it should be referred
2338  * in a human readable form.  This string should be UTF-8 and localized as it
2339  * expected that users will set it.
2340  *
2341  * In the Unity desktop the most prominent place that this is show will be
2342  * in the HUD.  HUD listings for this application indicator will start with
2343  * the title as the first part of the line for the menu items.
2344  *
2345  * Setting @title to %NULL removes the title.
2346  *
2347  * Since: 0.5
2348  *
2349  */
2350 void
app_indicator_set_title(AppIndicator * self,const gchar * title)2351 app_indicator_set_title (AppIndicator *self, const gchar * title)
2352 {
2353 	g_return_if_fail (IS_APP_INDICATOR (self));
2354 
2355 	g_object_set(G_OBJECT(self),
2356 	             PROP_TITLE_S, title == NULL ? "": title,
2357 	             NULL);
2358 
2359 	return;
2360 }
2361 
2362 /**
2363  * app_indicator_get_id:
2364  * @self: The #AppIndicator object to use
2365  *
2366  * Wrapper function for property #AppIndicator:id.
2367  *
2368  * Return value: The current ID
2369  */
2370 const gchar *
app_indicator_get_id(AppIndicator * self)2371 app_indicator_get_id (AppIndicator *self)
2372 {
2373   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2374 
2375   return self->priv->id;
2376 }
2377 
2378 /**
2379  * app_indicator_get_category:
2380  * @self: The #AppIndicator object to use
2381  *
2382  * Wrapper function for property #AppIndicator:category.
2383  *
2384  * Return value: The current category.
2385  */
2386 AppIndicatorCategory
app_indicator_get_category(AppIndicator * self)2387 app_indicator_get_category (AppIndicator *self)
2388 {
2389   g_return_val_if_fail (IS_APP_INDICATOR (self), APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
2390 
2391   return self->priv->category;
2392 }
2393 
2394 /**
2395  * app_indicator_get_status:
2396  * @self: The #AppIndicator object to use
2397  *
2398  * Wrapper function for property #AppIndicator:status.
2399  *
2400  * Return value: The current status.
2401  */
2402 AppIndicatorStatus
app_indicator_get_status(AppIndicator * self)2403 app_indicator_get_status (AppIndicator *self)
2404 {
2405   g_return_val_if_fail (IS_APP_INDICATOR (self), APP_INDICATOR_STATUS_PASSIVE);
2406 
2407   return self->priv->status;
2408 }
2409 
2410 /**
2411  * app_indicator_get_icon:
2412  * @self: The #AppIndicator object to use
2413  *
2414  * Wrapper function for property #AppIndicator:icon-name.
2415  *
2416  * Return value: The current icon name.
2417  */
2418 const gchar *
app_indicator_get_icon(AppIndicator * self)2419 app_indicator_get_icon (AppIndicator *self)
2420 {
2421   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2422 
2423   return self->priv->icon_name;
2424 }
2425 
2426 /**
2427  * app_indicator_get_icon_desc:
2428  * @self: The #AppIndicator object to use
2429  *
2430  * Wrapper function for property #AppIndicator:icon-desc.
2431  *
2432  * Return value: The current icon description.
2433 */
2434 const gchar *
app_indicator_get_icon_desc(AppIndicator * self)2435 app_indicator_get_icon_desc (AppIndicator *self)
2436 {
2437   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2438 
2439   return self->priv->accessible_desc;
2440 }
2441 
2442 /**
2443  * app_indicator_get_icon_theme_path:
2444  * @self: The #AppIndicator object to use
2445  *
2446  * Wrapper function for property #AppIndicator:icon-theme-path.
2447  *
2448  * Return value: The current icon theme path.
2449  */
2450 const gchar *
app_indicator_get_icon_theme_path(AppIndicator * self)2451 app_indicator_get_icon_theme_path (AppIndicator *self)
2452 {
2453   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2454 
2455   return self->priv->icon_theme_path;
2456 }
2457 
2458 /**
2459  * app_indicator_get_attention_icon:
2460  * @self: The #AppIndicator object to use
2461  *
2462  * Wrapper function for property #AppIndicator:attention-icon-name.
2463  *
2464  * Return value: The current attention icon name.
2465  */
2466 const gchar *
app_indicator_get_attention_icon(AppIndicator * self)2467 app_indicator_get_attention_icon (AppIndicator *self)
2468 {
2469   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2470 
2471   return self->priv->attention_icon_name;
2472 }
2473 
2474 /**
2475  * app_indicator_get_attention_icon_desc:
2476  * @self: The #AppIndicator object to use
2477  *
2478  * Wrapper function for property #AppIndicator:attention-icon-desc.
2479  *
2480  * Return value: The current attention icon description.
2481  */
2482 const gchar *
app_indicator_get_attention_icon_desc(AppIndicator * self)2483 app_indicator_get_attention_icon_desc (AppIndicator *self)
2484 {
2485   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2486 
2487   return self->priv->att_accessible_desc;
2488 }
2489 
2490 /**
2491  * app_indicator_get_title:
2492  * @self: The #AppIndicator object to use
2493  *
2494  * Gets the title of the application indicator.  See the function
2495  * app_indicator_set_title() for information on the title.
2496  *
2497  * Return value: The current title.
2498  *
2499  * Since: 0.5
2500  *
2501  */
2502 const gchar *
app_indicator_get_title(AppIndicator * self)2503 app_indicator_get_title (AppIndicator *self)
2504 {
2505 	g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2506 
2507 	return self->priv->title;
2508 }
2509 
2510 
2511 /**
2512  * app_indicator_get_menu:
2513  * @self: The #AppIndicator object to use
2514  *
2515  * Gets the menu being used for this application indicator.
2516  * Wrapper function for property #AppIndicator:menu.
2517  *
2518  * Returns: (transfer none): A #GtkMenu object or %NULL if one hasn't been set.
2519  */
2520 GtkMenu *
app_indicator_get_menu(AppIndicator * self)2521 app_indicator_get_menu (AppIndicator *self)
2522 {
2523 	AppIndicatorPrivate *priv;
2524 
2525 	g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2526 
2527 	priv = self->priv;
2528 
2529 	return GTK_MENU(priv->menu);
2530 }
2531 
2532 /**
2533  * app_indicator_get_label:
2534  * @self: The #AppIndicator object to use
2535  *
2536  * Wrapper function for property #AppIndicator:label.
2537  *
2538  * Return value: The current label.
2539  */
2540 const gchar *
app_indicator_get_label(AppIndicator * self)2541 app_indicator_get_label (AppIndicator *self)
2542 {
2543   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2544 
2545   return self->priv->label;
2546 }
2547 
2548 /**
2549  * app_indicator_get_label_guide:
2550  * @self: The #AppIndicator object to use
2551  *
2552  * Wrapper function for property #AppIndicator:label-guide.
2553  *
2554  * Return value: The current label guide.
2555  */
2556 const gchar *
app_indicator_get_label_guide(AppIndicator * self)2557 app_indicator_get_label_guide (AppIndicator *self)
2558 {
2559   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2560 
2561   return self->priv->label_guide;
2562 }
2563 
2564 /**
2565  * app_indicator_get_ordering_index:
2566  * @self: The #AppIndicator object to use
2567  *
2568  * Wrapper function for property #AppIndicator:ordering-index.
2569  *
2570  * Return value: The current ordering index.
2571  */
2572 guint32
app_indicator_get_ordering_index(AppIndicator * self)2573 app_indicator_get_ordering_index (AppIndicator *self)
2574 {
2575 	g_return_val_if_fail (IS_APP_INDICATOR (self), 0);
2576 
2577 	if (self->priv->ordering_index == 0) {
2578 		return _generate_id(self->priv->category, self->priv->id);
2579 	} else {
2580 		return self->priv->ordering_index;
2581 	}
2582 }
2583 
2584 /**
2585  * app_indicator_get_secondary_activate_target:
2586  * @self: The #AppIndicator object to use
2587  *
2588  * Gets the menuitem being called on secondary-activate event.
2589  *
2590  * Returns: (transfer none): A #GtkWidget object or %NULL if none has been set.
2591  */
2592 GtkWidget *
app_indicator_get_secondary_activate_target(AppIndicator * self)2593 app_indicator_get_secondary_activate_target (AppIndicator *self)
2594 {
2595 	g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2596 
2597 	return GTK_WIDGET(self->priv->sec_activate_target);
2598 }
2599 
2600 #define APP_INDICATOR_SHORTY_NICK "app-indicator-shorty-nick"
2601 
2602 /* Callback when an item from the desktop shortcuts gets
2603    called. */
2604 static void
shorty_activated_cb(DbusmenuMenuitem * mi,guint timestamp,gpointer user_data)2605 shorty_activated_cb (DbusmenuMenuitem * mi, guint timestamp, gpointer user_data)
2606 {
2607 	gchar * nick = g_object_get_data(G_OBJECT(mi), APP_INDICATOR_SHORTY_NICK);
2608 	g_return_if_fail(nick != NULL);
2609 
2610 	g_return_if_fail(IS_APP_INDICATOR(user_data));
2611 	AppIndicator * self = APP_INDICATOR(user_data);
2612 	AppIndicatorPrivate *priv = self->priv;
2613 
2614 	g_return_if_fail(priv->shorties != NULL);
2615 
2616 	indicator_desktop_shortcuts_nick_exec_with_context(priv->shorties, nick, NULL);
2617 
2618 	return;
2619 }
2620 
2621 /**
2622  * app_indicator_build_menu_from_desktop:
2623  * @self: The #AppIndicator object to use
2624  * @desktop_file: A path to the desktop file to build the menu from
2625  * @desktop_profile: Which entries should be used from the desktop file
2626  *
2627  * This function allows for building the Application Indicator menu
2628  * from a static desktop file.
2629  */
2630 void
app_indicator_build_menu_from_desktop(AppIndicator * self,const gchar * desktop_file,const gchar * desktop_profile)2631 app_indicator_build_menu_from_desktop (AppIndicator * self, const gchar * desktop_file, const gchar * desktop_profile)
2632 {
2633 	g_return_if_fail(IS_APP_INDICATOR(self));
2634 	AppIndicatorPrivate *priv = self->priv;
2635 
2636 	/* Build a new shortcuts object */
2637 	if (priv->shorties != NULL) {
2638 		g_object_unref(priv->shorties);
2639 		priv->shorties = NULL;
2640 	}
2641 	priv->shorties = indicator_desktop_shortcuts_new(desktop_file, desktop_profile);
2642 	g_return_if_fail(priv->shorties != NULL);
2643 
2644 	const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(priv->shorties);
2645 	int nick_num;
2646 
2647 	/* Place the items on a dbusmenu */
2648 	DbusmenuMenuitem * root = dbusmenu_menuitem_new();
2649 
2650 	for (nick_num = 0; nicks[nick_num] != NULL; nick_num++) {
2651 		DbusmenuMenuitem * item = dbusmenu_menuitem_new();
2652 		g_object_set_data(G_OBJECT(item), APP_INDICATOR_SHORTY_NICK, (gpointer)nicks[nick_num]);
2653 
2654 		gchar * name = indicator_desktop_shortcuts_nick_get_name(priv->shorties, nicks[nick_num]);
2655 		dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, name);
2656 		g_free(name);
2657 
2658 		g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(shorty_activated_cb), self);
2659 
2660 		dbusmenu_menuitem_child_append(root, item);
2661 	}
2662 
2663 	/* Swap it if needed */
2664 	if (priv->menuservice == NULL) {
2665 		gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s/Menu", priv->clean_id);
2666 		priv->menuservice = dbusmenu_server_new (path);
2667 		g_free(path);
2668 	}
2669 
2670 	dbusmenu_server_set_root (priv->menuservice, root);
2671 
2672 	if (priv->menu != NULL) {
2673 		g_object_unref(G_OBJECT(priv->menu));
2674 		priv->menu = NULL;
2675 	}
2676 
2677 	return;
2678 }
2679