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