~indicator-applet-developers/indicator-application/trunk.17.04

« back to all changes in this revision

Viewing changes to src/app-indicator.c

  • Committer: Ted Gould
  • Date: 2010-12-08 20:08:44 UTC
  • mfrom: (166.2.17 remove-library)
  • Revision ID: ted@gould.cx-20101208200844-n8co8s6dttdlg8h3
Remove the library from the build

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 <dbus/dbus-glib.h>
35
 
#include <dbus/dbus-glib-bindings.h>
36
 
 
37
 
#include <libdbusmenu-glib/server.h>
38
 
#ifdef HAVE_GTK3
39
 
#include <libdbusmenu-gtk3/client.h>
40
 
#else
41
 
#include <libdbusmenu-gtk/client.h>
42
 
#endif
43
 
 
44
 
#include "app-indicator.h"
45
 
#include "app-indicator-enum-types.h"
46
 
#include "application-service-marshal.h"
47
 
 
48
 
#include "notification-item-server.h"
49
 
#include "notification-watcher-client.h"
50
 
 
51
 
#include "dbus-shared.h"
52
 
#include "generate-id.h"
53
 
 
54
 
#define PANEL_ICON_SUFFIX  "panel"
55
 
 
56
 
/**
57
 
        AppIndicatorPrivate:
58
 
 
59
 
        All of the private data in an instance of a
60
 
        application indicator.
61
 
*/
62
 
/*  Private Fields
63
 
        @id: The ID of the indicator.  Maps to AppIndicator:id.
64
 
        @category: Which category the indicator is.  Maps to AppIndicator:category.
65
 
        @status: The status of the indicator.  Maps to AppIndicator:status.
66
 
        @icon_name: The name of the icon to use.  Maps to AppIndicator:icon-name.
67
 
        @attention_icon_name: The name of the attention icon to use.  Maps to AppIndicator:attention-icon-name.
68
 
        @menu: The menu for this indicator.  Maps to AppIndicator:menu
69
 
        @watcher_proxy: The proxy connection to the watcher we're connected to.  If we're not connected to one this will be %NULL.
70
 
*/
71
 
struct _AppIndicatorPrivate {
72
 
        /*< Private >*/
73
 
        /* Properties */
74
 
        gchar                *id;
75
 
        gchar                *clean_id;
76
 
        AppIndicatorCategory  category;
77
 
        AppIndicatorStatus    status;
78
 
        gchar                *icon_name;
79
 
        gchar                *attention_icon_name;
80
 
        gchar                *icon_theme_path;
81
 
        DbusmenuServer       *menuservice;
82
 
        GtkWidget            *menu;
83
 
        guint32               ordering_index;
84
 
        gchar *               label;
85
 
        gchar *               label_guide;
86
 
        guint                 label_change_idle;
87
 
 
88
 
        GtkStatusIcon *       status_icon;
89
 
        gint                  fallback_timer;
90
 
 
91
 
        /* Fun stuff */
92
 
        DBusGProxy           *watcher_proxy;
93
 
        DBusGConnection      *connection;
94
 
        DBusGProxy *          dbus_proxy;
95
 
};
96
 
 
97
 
/* Signals Stuff */
98
 
enum {
99
 
        NEW_ICON,
100
 
        NEW_ATTENTION_ICON,
101
 
        NEW_STATUS,
102
 
        NEW_LABEL,
103
 
        X_NEW_LABEL,
104
 
        CONNECTION_CHANGED,
105
 
    NEW_ICON_THEME_PATH,
106
 
        LAST_SIGNAL
107
 
};
108
 
 
109
 
static guint signals[LAST_SIGNAL] = { 0 };
110
 
 
111
 
/* Enum for the properties so that they can be quickly
112
 
   found and looked up. */
113
 
enum {
114
 
        PROP_0,
115
 
        PROP_ID,
116
 
        PROP_CATEGORY,
117
 
        PROP_STATUS,
118
 
        PROP_ICON_NAME,
119
 
        PROP_ATTENTION_ICON_NAME,
120
 
        PROP_ICON_THEME_PATH,
121
 
        PROP_MENU,
122
 
        PROP_CONNECTED,
123
 
        PROP_LABEL,
124
 
        PROP_LABEL_GUIDE,
125
 
        PROP_X_LABEL,
126
 
        PROP_X_LABEL_GUIDE,
127
 
        PROP_ORDERING_INDEX,
128
 
        PROP_X_ORDERING_INDEX
129
 
};
130
 
 
131
 
/* The strings so that they can be slowly looked up. */
132
 
#define PROP_ID_S                    "id"
133
 
#define PROP_CATEGORY_S              "category"
134
 
#define PROP_STATUS_S                "status"
135
 
#define PROP_ICON_NAME_S             "icon-name"
136
 
#define PROP_ATTENTION_ICON_NAME_S   "attention-icon-name"
137
 
#define PROP_ICON_THEME_PATH_S       "icon-theme-path"
138
 
#define PROP_MENU_S                  "menu"
139
 
#define PROP_CONNECTED_S             "connected"
140
 
#define PROP_LABEL_S                 "label"
141
 
#define PROP_LABEL_GUIDE_S           "label-guide"
142
 
#define PROP_X_LABEL_S               ("x-ayatana-" PROP_LABEL_S)
143
 
#define PROP_X_LABEL_GUIDE_S         ("x-ayatana-" PROP_LABEL_GUIDE_S)
144
 
#define PROP_ORDERING_INDEX_S        "ordering-index"
145
 
#define PROP_X_ORDERING_INDEX_S      ("x-ayatana-" PROP_ORDERING_INDEX_S)
146
 
 
147
 
/* Private macro, shhhh! */
148
 
#define APP_INDICATOR_GET_PRIVATE(o) \
149
 
                             (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_INDICATOR_TYPE, AppIndicatorPrivate))
150
 
 
151
 
/* Signal wrapper */
152
 
#define APP_INDICATOR_SIGNAL_X_NEW_LABEL ("x-ayatana-" APP_INDICATOR_SIGNAL_NEW_LABEL)
153
 
 
154
 
/* Default Path */
155
 
#define DEFAULT_ITEM_PATH   "/org/ayatana/NotificationItem"
156
 
 
157
 
/* More constants */
158
 
#define DEFAULT_FALLBACK_TIMER  100 /* in milliseconds */
159
 
 
160
 
/* Boiler plate */
161
 
static void app_indicator_class_init (AppIndicatorClass *klass);
162
 
static void app_indicator_init       (AppIndicator *self);
163
 
static void app_indicator_dispose    (GObject *object);
164
 
static void app_indicator_finalize   (GObject *object);
165
 
/* Property functions */
166
 
static void app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
167
 
static void app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
168
 
/* Other stuff */
169
 
static void signal_label_change (AppIndicator * self);
170
 
static void check_connect (AppIndicator * self);
171
 
static void register_service_cb (DBusGProxy * proxy, GError * error, gpointer data);
172
 
static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout);
173
 
static gboolean fallback_timer_expire (gpointer data);
174
 
static GtkStatusIcon * fallback (AppIndicator * self);
175
 
static void status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data);
176
 
static void status_icon_changes (AppIndicator * self, gpointer data);
177
 
static void status_icon_activate (GtkStatusIcon * icon, gpointer data);
178
 
static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon);
179
 
static gchar * append_panel_icon_suffix (const gchar * icon_name);
180
 
static void watcher_proxy_destroyed (GObject * object, gpointer data);
181
 
static void client_menu_changed (GtkWidget *widget, GtkWidget *child, AppIndicator *indicator);
182
 
static void submenu_changed (GtkWidget *widget, GtkWidget *child, gpointer data);
183
 
 
184
 
static void theme_changed_cb (GtkIconTheme * theme, gpointer user_data);
185
 
 
186
 
/* GObject type */
187
 
G_DEFINE_TYPE (AppIndicator, app_indicator, G_TYPE_OBJECT);
188
 
 
189
 
static void
190
 
app_indicator_class_init (AppIndicatorClass *klass)
191
 
{
192
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
193
 
 
194
 
        g_type_class_add_private (klass, sizeof (AppIndicatorPrivate));
195
 
 
196
 
        /* Clean up */
197
 
        object_class->dispose = app_indicator_dispose;
198
 
        object_class->finalize = app_indicator_finalize;
199
 
 
200
 
        /* Property funcs */
201
 
        object_class->set_property = app_indicator_set_property;
202
 
        object_class->get_property = app_indicator_get_property;
203
 
 
204
 
        /* Our own funcs */
205
 
        klass->fallback = fallback;
206
 
        klass->unfallback = unfallback;
207
 
 
208
 
        /* Properties */
209
 
 
210
 
        /**
211
 
                AppIndicator:id:
212
 
                
213
 
                The ID for this indicator, which should be unique, but used consistently
214
 
                by this program and its indicator.
215
 
        */
216
 
        g_object_class_install_property (object_class,
217
 
                                         PROP_ID,
218
 
                                         g_param_spec_string(PROP_ID_S,
219
 
                                                             "The ID for this indicator",
220
 
                                                             "An ID that should be unique, but used consistently by this program and its indicator.",
221
 
                                                             NULL,
222
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
223
 
 
224
 
        /**
225
 
                AppIndicator:category:
226
 
                
227
 
                The type of indicator that this represents.  Please don't use 'Other'. 
228
 
                Defaults to 'ApplicationStatus'.
229
 
        */
230
 
        g_object_class_install_property (object_class,
231
 
                                         PROP_CATEGORY,
232
 
                                         g_param_spec_string (PROP_CATEGORY_S,
233
 
                                                              "Indicator Category",
234
 
                                                              "The type of indicator that this represents.  Please don't use 'other'. Defaults to 'ApplicationStatus'.",
235
 
                                                              NULL,
236
 
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
237
 
 
238
 
        /**
239
 
                AppIndicator:status:
240
 
                
241
 
                Whether the indicator is shown or requests attention. Defaults to
242
 
                'Passive'.
243
 
        */
244
 
        g_object_class_install_property (object_class,
245
 
                                         PROP_STATUS,
246
 
                                         g_param_spec_string (PROP_STATUS_S,
247
 
                                                              "Indicator Status",
248
 
                                                              "Whether the indicator is shown or requests attention. Defaults to 'Passive'.",
249
 
                                                              NULL,
250
 
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
251
 
 
252
 
        /**
253
 
                AppIndicator:icon-name:
254
 
                
255
 
                The name of the regular icon that is shown for the indicator.
256
 
        */
257
 
        g_object_class_install_property(object_class,
258
 
                                    PROP_ICON_NAME,
259
 
                                        g_param_spec_string (PROP_ICON_NAME_S,
260
 
                                                             "An icon for the indicator",
261
 
                                                             "The default icon that is shown for the indicator.",
262
 
                                                             NULL,
263
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
264
 
 
265
 
        /**
266
 
                AppIndicator:attention-icon-name:
267
 
                
268
 
                If the indicator sets it's status to %APP_INDICATOR_STATUS_ATTENTION
269
 
                then this icon is shown.
270
 
        */
271
 
        g_object_class_install_property (object_class,
272
 
                                         PROP_ATTENTION_ICON_NAME,
273
 
                                         g_param_spec_string (PROP_ATTENTION_ICON_NAME_S,
274
 
                                                              "An icon to show when the indicator request attention.",
275
 
                                                              "If the indicator sets it's status to 'attention' then this icon is shown.",
276
 
                                                              NULL,
277
 
                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278
 
        /**
279
 
                AppIndicator:icon-theme-path:
280
 
                
281
 
                An additional place to look for icon names that may be installed by the
282
 
                application.
283
 
        */
284
 
        g_object_class_install_property(object_class,
285
 
                                        PROP_ICON_THEME_PATH,
286
 
                                        g_param_spec_string (PROP_ICON_THEME_PATH_S,
287
 
                                                             "An additional path for custom icons.",
288
 
                                                             "An additional place to look for icon names that may be installed by the application.",
289
 
                                                             NULL,
290
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
291
 
        
292
 
        /**
293
 
                AppIndicator:menu:
294
 
                
295
 
                A method for getting the menu path as a string for DBus.
296
 
        */
297
 
    g_object_class_install_property(object_class,
298
 
                                        PROP_MENU,
299
 
                                        g_param_spec_boxed (PROP_MENU_S,
300
 
                                                             "The object path of the menu on DBus.",
301
 
                                                             "A method for getting the menu path as a string for DBus.",
302
 
                                                             DBUS_TYPE_G_OBJECT_PATH,
303
 
                                                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
304
 
 
305
 
        /**
306
 
                AppIndicator:connected:
307
 
                
308
 
                Pretty simple, %TRUE if we have a reasonable expectation of being 
309
 
                displayed through this object. You should hide your TrayIcon if so.
310
 
        */
311
 
        g_object_class_install_property (object_class,
312
 
                                         PROP_CONNECTED,
313
 
                                         g_param_spec_boolean (PROP_CONNECTED_S,
314
 
                                                               "Whether we're conneced to a watcher",
315
 
                                                               "Pretty simple, true if we have a reasonable expectation of being displayed through this object.  You should hide your TrayIcon if so.",
316
 
                                                               FALSE,
317
 
                                                               G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
318
 
        /**
319
 
                AppIndicator:label:
320
 
                
321
 
                A label that can be shown next to the string in the application
322
 
                indicator.  The label will not be shown unless there is an icon
323
 
                as well.  The label is useful for numerical and other frequently
324
 
                updated information.  In general, it shouldn't be shown unless a
325
 
                user requests it as it can take up a significant amount of space
326
 
                on the user's panel.  This may not be shown in all visualizations.
327
 
        */
328
 
        g_object_class_install_property(object_class,
329
 
                                        PROP_LABEL,
330
 
                                        g_param_spec_string (PROP_LABEL_S,
331
 
                                                             "A label next to the icon",
332
 
                                                             "A label to provide dynamic information.",
333
 
                                                             NULL,
334
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
335
 
        /**
336
 
                AppIndicator:label-guide:
337
 
                
338
 
                An optional string to provide guidance to the panel on how big
339
 
                the #AppIndicator:label string could get.  If this is set correctly
340
 
                then the panel should never 'jiggle' as the string adjusts through
341
 
                out the range of options.  For instance, if you were providing a
342
 
                percentage like "54% thrust" in #AppIndicator:label you'd want to
343
 
                set this string to "100% thrust" to ensure space when Scotty can
344
 
                get you enough power.
345
 
        */
346
 
        g_object_class_install_property(object_class,
347
 
                                        PROP_LABEL_GUIDE,
348
 
                                        g_param_spec_string (PROP_LABEL_GUIDE_S,
349
 
                                                             "A string to size the space available for the label.",
350
 
                                                             "To ensure that the label does not cause the panel to 'jiggle' this string should provide information on how much space it could take.",
351
 
                                                             NULL,
352
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
353
 
        /**
354
 
                AppIndicator:ordering-index:
355
 
 
356
 
                The ordering index is an odd parameter, and if you think you don't need
357
 
                it you're probably right.  In general, the application indicator try
358
 
                to place the applications in a recreatable place taking into account
359
 
                which category they're in to try and group them.  But, there are some
360
 
                cases where you'd want to ensure indicators are next to each other.
361
 
                To do that you can override the generated ordering index and replace it
362
 
                with a new one.  Again, you probably don't want to be doing this, but
363
 
                in case you do, this is the way.
364
 
        */
365
 
        g_object_class_install_property(object_class,
366
 
                                        PROP_ORDERING_INDEX,
367
 
                                        g_param_spec_uint (PROP_ORDERING_INDEX_S,
368
 
                                                           "The location that this app indicator should be in the list.",
369
 
                                                           "A way to override the default ordering of the applications by providing a very specific idea of where this entry should be placed.",
370
 
                                                           0, G_MAXUINT32, 0,
371
 
                                                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
372
 
        /**
373
 
                AppIndicator:x-ayatana-ordering-index:
374
 
 
375
 
                A wrapper for #AppIndicator:ordering-index so that it can match the
376
 
                dbus interface currently.  It will hopefully be retired, please don't
377
 
                use it anywhere.
378
 
        */
379
 
        g_object_class_install_property(object_class,
380
 
                                        PROP_X_ORDERING_INDEX,
381
 
                                        g_param_spec_uint (PROP_X_ORDERING_INDEX_S,
382
 
                                                           "A wrapper, please don't use.",
383
 
                                                           "A wrapper, please don't use.",
384
 
                                                           0, G_MAXUINT32, 0,
385
 
                                                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
386
 
 
387
 
        /**
388
 
                AppIndicator:x-ayatana-label:
389
 
 
390
 
                Wrapper for #AppIndicator:label.  Please use that in all of your
391
 
                code.
392
 
        */
393
 
        g_object_class_install_property(object_class,
394
 
                                        PROP_X_LABEL,
395
 
                                        g_param_spec_string (PROP_X_LABEL_S,
396
 
                                                             "A wrapper, please don't use.",
397
 
                                                             "A wrapper, please don't use.",
398
 
                                                             NULL,
399
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
400
 
        /**
401
 
                AppIndicator:x-ayatana-label-guide:
402
 
 
403
 
                Wrapper for #AppIndicator:label-guide.  Please use that in all of your
404
 
                code.
405
 
        */
406
 
        g_object_class_install_property(object_class,
407
 
                                        PROP_X_LABEL_GUIDE,
408
 
                                        g_param_spec_string (PROP_X_LABEL_GUIDE_S,
409
 
                                                             "A wrapper, please don't use.",
410
 
                                                             "A wrapper, please don't use.",
411
 
                                                             NULL,
412
 
                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
413
 
 
414
 
        /* Signals */
415
 
 
416
 
        /**
417
 
                AppIndicator::new-icon:
418
 
                @arg0: The #AppIndicator object
419
 
 
420
 
                Emitted when #AppIndicator:icon-name is changed
421
 
        */
422
 
        signals[NEW_ICON] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON,
423
 
                                          G_TYPE_FROM_CLASS(klass),
424
 
                                          G_SIGNAL_RUN_LAST,
425
 
                                          G_STRUCT_OFFSET (AppIndicatorClass, new_icon),
426
 
                                          NULL, NULL,
427
 
                                          g_cclosure_marshal_VOID__VOID,
428
 
                                          G_TYPE_NONE, 0, G_TYPE_NONE);
429
 
 
430
 
        /**
431
 
                AppIndicator::new-attention-icon:
432
 
                @arg0: The #AppIndicator object
433
 
 
434
 
                Emitted when #AppIndicator:attention-icon-name is changed
435
 
        */
436
 
        signals[NEW_ATTENTION_ICON] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON,
437
 
                                                    G_TYPE_FROM_CLASS(klass),
438
 
                                                    G_SIGNAL_RUN_LAST,
439
 
                                                    G_STRUCT_OFFSET (AppIndicatorClass, new_attention_icon),
440
 
                                                    NULL, NULL,
441
 
                                                    g_cclosure_marshal_VOID__VOID,
442
 
                                                    G_TYPE_NONE, 0, G_TYPE_NONE);
443
 
 
444
 
        /**
445
 
                AppIndicator::new-status:
446
 
                @arg0: The #AppIndicator object
447
 
                @arg1: The string value of the #AppIndicatorStatus enum.
448
 
 
449
 
                Emitted when #AppIndicator:status is changed
450
 
        */
451
 
        signals[NEW_STATUS] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_STATUS,
452
 
                                            G_TYPE_FROM_CLASS(klass),
453
 
                                            G_SIGNAL_RUN_LAST,
454
 
                                            G_STRUCT_OFFSET (AppIndicatorClass, new_status),
455
 
                                            NULL, NULL,
456
 
                                            g_cclosure_marshal_VOID__STRING,
457
 
                                            G_TYPE_NONE, 1,
458
 
                                            G_TYPE_STRING);
459
 
 
460
 
        /**
461
 
                AppIndicator::new-label:
462
 
                @arg0: The #AppIndicator object
463
 
                @arg1: The string for the label
464
 
                @arg1: The string for the guide
465
 
 
466
 
                Emitted when either #AppIndicator:label or #AppIndicator:label-guide are
467
 
                changed.
468
 
        */
469
 
        signals[NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_LABEL,
470
 
                                            G_TYPE_FROM_CLASS(klass),
471
 
                                            G_SIGNAL_RUN_LAST,
472
 
                                            G_STRUCT_OFFSET (AppIndicatorClass, new_label),
473
 
                                            NULL, NULL,
474
 
                                            _application_service_marshal_VOID__STRING_STRING,
475
 
                                            G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
476
 
 
477
 
        /**
478
 
                AppIndicator::x-ayatana-new-label:
479
 
                @arg0: The #AppIndicator object
480
 
                @arg1: The string for the label
481
 
                @arg1: The string for the guide
482
 
 
483
 
                Wrapper for #AppIndicator::new-label, please don't use this signal
484
 
                use the other one.
485
 
        */
486
 
        signals[X_NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_X_NEW_LABEL,
487
 
                                            G_TYPE_FROM_CLASS(klass),
488
 
                                            G_SIGNAL_RUN_LAST,
489
 
                                            G_STRUCT_OFFSET (AppIndicatorClass, new_label),
490
 
                                            NULL, NULL,
491
 
                                            _application_service_marshal_VOID__STRING_STRING,
492
 
                                            G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
493
 
 
494
 
        /**
495
 
                AppIndicator::connection-changed:
496
 
                @arg0: The #AppIndicator object
497
 
                @arg1: Whether we're connected or not
498
 
 
499
 
                Signaled when we connect to a watcher, or when it drops away.
500
 
        */
501
 
        signals[CONNECTION_CHANGED] = g_signal_new (APP_INDICATOR_SIGNAL_CONNECTION_CHANGED,
502
 
                                                    G_TYPE_FROM_CLASS(klass),
503
 
                                                    G_SIGNAL_RUN_LAST,
504
 
                                                    G_STRUCT_OFFSET (AppIndicatorClass, connection_changed),
505
 
                                                    NULL, NULL,
506
 
                                                    g_cclosure_marshal_VOID__BOOLEAN,
507
 
                                                    G_TYPE_NONE, 1, G_TYPE_BOOLEAN, G_TYPE_NONE);
508
 
 
509
 
        /**
510
 
                AppIndicator::new-icon-theme-path:
511
 
                @arg0: The #AppIndicator object
512
 
 
513
 
                Signaled when there is a new icon set for the
514
 
                object.
515
 
        */
516
 
        signals[NEW_ICON_THEME_PATH] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH,
517
 
                                          G_TYPE_FROM_CLASS(klass),
518
 
                                          G_SIGNAL_RUN_LAST,
519
 
                                          G_STRUCT_OFFSET (AppIndicatorClass, new_icon_theme_path),
520
 
                                          NULL, NULL,
521
 
                                          g_cclosure_marshal_VOID__STRING,
522
 
                                          G_TYPE_NONE, 1, G_TYPE_STRING);
523
 
 
524
 
        /* Initialize the object as a DBus type */
525
 
        dbus_g_object_type_install_info(APP_INDICATOR_TYPE,
526
 
                                        &dbus_glib__notification_item_server_object_info);
527
 
 
528
 
        return;
529
 
}
530
 
 
531
 
static void
532
 
app_indicator_init (AppIndicator *self)
533
 
{
534
 
        AppIndicatorPrivate * priv = APP_INDICATOR_GET_PRIVATE(self);
535
 
 
536
 
        priv->id = NULL;
537
 
        priv->clean_id = NULL;
538
 
        priv->category = APP_INDICATOR_CATEGORY_OTHER;
539
 
        priv->status = APP_INDICATOR_STATUS_PASSIVE;
540
 
        priv->icon_name = NULL;
541
 
        priv->attention_icon_name = NULL;
542
 
        priv->icon_theme_path = NULL;
543
 
        priv->menu = NULL;
544
 
        priv->menuservice = NULL;
545
 
        priv->ordering_index = 0;
546
 
        priv->label = NULL;
547
 
        priv->label_guide = NULL;
548
 
        priv->label_change_idle = 0;
549
 
 
550
 
        priv->watcher_proxy = NULL;
551
 
        priv->connection = NULL;
552
 
        priv->dbus_proxy = NULL;
553
 
 
554
 
        priv->status_icon = NULL;
555
 
        priv->fallback_timer = 0;
556
 
 
557
 
        /* Put the object on DBus */
558
 
        GError * error = NULL;
559
 
        priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
560
 
        if (error != NULL) {
561
 
                g_error("Unable to connect to the session bus when creating application indicator: %s", error->message);
562
 
                g_error_free(error);
563
 
                return;
564
 
        }
565
 
        dbus_g_connection_ref(priv->connection);
566
 
 
567
 
        g_signal_connect(G_OBJECT(gtk_icon_theme_get_default()),
568
 
                "changed", G_CALLBACK(theme_changed_cb), self);
569
 
 
570
 
        self->priv = priv;
571
 
 
572
 
        return;
573
 
}
574
 
 
575
 
/* Free all objects, make sure that all the dbus
576
 
   signals are sent out before we shut this down. */
577
 
static void
578
 
app_indicator_dispose (GObject *object)
579
 
{
580
 
        AppIndicator *self = APP_INDICATOR (object);
581
 
        AppIndicatorPrivate *priv = self->priv;
582
 
 
583
 
        if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
584
 
                app_indicator_set_status(self, APP_INDICATOR_STATUS_PASSIVE);
585
 
        }
586
 
 
587
 
        if (priv->status_icon != NULL) {
588
 
                AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(object);
589
 
                if (class->unfallback != NULL) {
590
 
                        class->unfallback(self, priv->status_icon);
591
 
                }
592
 
                priv->status_icon = NULL;
593
 
        }
594
 
 
595
 
        if (priv->fallback_timer != 0) {
596
 
                g_source_remove(priv->fallback_timer);
597
 
                priv->fallback_timer = 0;
598
 
        }
599
 
 
600
 
        if (priv->label_change_idle != 0) {
601
 
                g_source_remove(priv->label_change_idle);
602
 
                priv->label_change_idle = 0;
603
 
        }
604
 
 
605
 
        if (priv->menu != NULL) {
606
 
                g_signal_handlers_disconnect_by_func (G_OBJECT (priv->menu),
607
 
                                                      client_menu_changed,
608
 
                                                      self);
609
 
                g_object_unref(G_OBJECT(priv->menu));
610
 
                priv->menu = NULL;
611
 
        }
612
 
 
613
 
        if (priv->menuservice != NULL) {
614
 
                g_object_unref (priv->menuservice);
615
 
        }
616
 
 
617
 
        if (priv->dbus_proxy != NULL) {
618
 
                g_object_unref(G_OBJECT(priv->dbus_proxy));
619
 
                priv->dbus_proxy = NULL;
620
 
        }
621
 
 
622
 
        if (priv->watcher_proxy != NULL) {
623
 
                dbus_g_connection_flush(priv->connection);
624
 
                g_signal_handlers_disconnect_by_func(G_OBJECT(priv->watcher_proxy), watcher_proxy_destroyed, self);
625
 
                g_object_unref(G_OBJECT(priv->watcher_proxy));
626
 
                priv->watcher_proxy = NULL;
627
 
 
628
 
            /* Emit the AppIndicator::connection-changed signal*/
629
 
        g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
630
 
        }
631
 
 
632
 
        if (priv->connection != NULL) {
633
 
                dbus_g_connection_unref(priv->connection);
634
 
                priv->connection = NULL;
635
 
        }
636
 
 
637
 
        G_OBJECT_CLASS (app_indicator_parent_class)->dispose (object);
638
 
        return;
639
 
}
640
 
 
641
 
/* Free all of the memory that we could be using in
642
 
   the object. */
643
 
static void
644
 
app_indicator_finalize (GObject *object)
645
 
{
646
 
        AppIndicator * self = APP_INDICATOR(object);
647
 
        AppIndicatorPrivate *priv = self->priv;
648
 
 
649
 
        if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
650
 
                g_warning("Finalizing Application Status with the status set to: %d", priv->status);
651
 
        }
652
 
 
653
 
        if (priv->id != NULL) {
654
 
                g_free(priv->id);
655
 
                priv->id = NULL;
656
 
        }
657
 
 
658
 
        if (priv->clean_id != NULL) {
659
 
                g_free(priv->clean_id);
660
 
                priv->clean_id = NULL;
661
 
        }
662
 
 
663
 
        if (priv->icon_name != NULL) {
664
 
                g_free(priv->icon_name);
665
 
                priv->icon_name = NULL;
666
 
        }
667
 
 
668
 
        if (priv->attention_icon_name != NULL) {
669
 
                g_free(priv->attention_icon_name);
670
 
                priv->attention_icon_name = NULL;
671
 
        }
672
 
 
673
 
        if (priv->icon_theme_path != NULL) {
674
 
                g_free(priv->icon_theme_path);
675
 
                priv->icon_theme_path = NULL;
676
 
        }
677
 
        
678
 
        if (priv->label != NULL) {
679
 
                g_free(priv->label);
680
 
                priv->label = NULL;
681
 
        }
682
 
 
683
 
        if (priv->label_guide != NULL) {
684
 
                g_free(priv->label_guide);
685
 
                priv->label_guide = NULL;
686
 
        }
687
 
 
688
 
        G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object);
689
 
        return;
690
 
}
691
 
 
692
 
#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))
693
 
 
694
 
/* Set some properties */
695
 
static void
696
 
app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
697
 
{
698
 
        AppIndicator *self = APP_INDICATOR (object);
699
 
        AppIndicatorPrivate *priv = self->priv;
700
 
        GEnumValue *enum_val;
701
 
 
702
 
        switch (prop_id) {
703
 
        case PROP_ID:
704
 
          if (priv->id != NULL) {
705
 
            g_warning ("Resetting ID value when I already had a value of: %s", priv->id);
706
 
            break;
707
 
          }
708
 
 
709
 
          priv->id = g_strdup (g_value_get_string (value));
710
 
 
711
 
          priv->clean_id = g_strdup(priv->id);
712
 
          gchar * cleaner;
713
 
          for (cleaner = priv->clean_id; *cleaner != '\0'; cleaner++) {
714
 
            if (!g_ascii_isalnum(*cleaner)) {
715
 
              *cleaner = '_';
716
 
            }
717
 
          }
718
 
 
719
 
          check_connect (self);
720
 
          break;
721
 
 
722
 
        case PROP_CATEGORY:
723
 
          enum_val = g_enum_get_value_by_nick ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY),
724
 
                                               g_value_get_string (value));
725
 
 
726
 
          if (priv->category != enum_val->value)
727
 
            {
728
 
              priv->category = enum_val->value;
729
 
            }
730
 
 
731
 
          break;
732
 
 
733
 
        case PROP_STATUS:
734
 
          enum_val = g_enum_get_value_by_nick ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS),
735
 
                                               g_value_get_string (value));
736
 
 
737
 
          app_indicator_set_status (APP_INDICATOR (object),
738
 
                                    enum_val->value);
739
 
          break;
740
 
 
741
 
        case PROP_ICON_NAME:
742
 
          app_indicator_set_icon (APP_INDICATOR (object),
743
 
                                  g_value_get_string (value));
744
 
          check_connect (self);
745
 
          break;
746
 
 
747
 
        case PROP_ATTENTION_ICON_NAME:
748
 
          app_indicator_set_attention_icon (APP_INDICATOR (object),
749
 
                                            g_value_get_string (value));
750
 
          break;
751
 
 
752
 
        case PROP_ICON_THEME_PATH:
753
 
          app_indicator_set_icon_theme_path (APP_INDICATOR (object),
754
 
                                            g_value_get_string (value));
755
 
          check_connect (self);
756
 
          break;
757
 
 
758
 
                case PROP_X_LABEL:
759
 
                case PROP_LABEL: {
760
 
                  gchar * oldlabel = priv->label;
761
 
                  priv->label = g_value_dup_string(value);
762
 
 
763
 
                  if (g_strcmp0(oldlabel, priv->label) != 0) {
764
 
                    signal_label_change(APP_INDICATOR(object));
765
 
                  }
766
 
 
767
 
                  if (priv->label != NULL && priv->label[0] == '\0') {
768
 
                        g_free(priv->label);
769
 
                        priv->label = NULL;
770
 
                  }
771
 
 
772
 
                  if (oldlabel != NULL) {
773
 
                        g_free(oldlabel);
774
 
                  }
775
 
                  break;
776
 
                }
777
 
                case PROP_X_LABEL_GUIDE:
778
 
                case PROP_LABEL_GUIDE: {
779
 
                  gchar * oldguide = priv->label_guide;
780
 
                  priv->label_guide = g_value_dup_string(value);
781
 
 
782
 
                  if (g_strcmp0(oldguide, priv->label_guide) != 0) {
783
 
                    signal_label_change(APP_INDICATOR(object));
784
 
                  }
785
 
 
786
 
                  if (priv->label_guide != NULL && priv->label_guide[0] == '\0') {
787
 
                        g_free(priv->label_guide);
788
 
                        priv->label_guide = NULL;
789
 
                  }
790
 
 
791
 
                  if (oldguide != NULL) {
792
 
                        g_free(oldguide);
793
 
                  }
794
 
                  break;
795
 
                }
796
 
                case PROP_X_ORDERING_INDEX:
797
 
                case PROP_ORDERING_INDEX:
798
 
                  priv->ordering_index = g_value_get_uint(value);
799
 
                  break;
800
 
 
801
 
        default:
802
 
          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
803
 
          break;
804
 
        }
805
 
 
806
 
        return;
807
 
}
808
 
 
809
 
/* Function to fill our value with the property it's requesting. */
810
 
static void
811
 
app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
812
 
{
813
 
        AppIndicator *self = APP_INDICATOR (object);
814
 
        AppIndicatorPrivate *priv = self->priv;
815
 
        GEnumValue *enum_value;
816
 
 
817
 
        switch (prop_id) {
818
 
        case PROP_ID:
819
 
          g_value_set_string (value, priv->id);
820
 
          break;
821
 
 
822
 
        case PROP_CATEGORY:
823
 
          enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), priv->category);
824
 
          g_value_set_string (value, enum_value->value_nick);
825
 
          break;
826
 
 
827
 
        case PROP_STATUS:
828
 
          enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), priv->status);
829
 
          g_value_set_string (value, enum_value->value_nick);
830
 
          break;
831
 
 
832
 
        case PROP_ICON_NAME:
833
 
          g_value_set_string (value, priv->icon_name);
834
 
          break;
835
 
 
836
 
        case PROP_ATTENTION_ICON_NAME:
837
 
          g_value_set_string (value, priv->attention_icon_name);
838
 
          break;
839
 
 
840
 
        case PROP_ICON_THEME_PATH:
841
 
          g_value_set_string (value, priv->icon_theme_path);
842
 
          break;
843
 
 
844
 
        case PROP_MENU:
845
 
          if (priv->menuservice != NULL) {
846
 
            GValue strval = { 0 };
847
 
            g_value_init(&strval, G_TYPE_STRING);
848
 
            g_object_get_property (G_OBJECT (priv->menuservice), DBUSMENU_SERVER_PROP_DBUS_OBJECT, &strval);
849
 
            g_value_set_boxed(value, g_value_get_string(&strval));
850
 
            g_value_unset(&strval);
851
 
          }
852
 
          break;
853
 
 
854
 
        case PROP_CONNECTED:
855
 
          g_value_set_boolean (value, priv->watcher_proxy != NULL ? TRUE : FALSE);
856
 
          break;
857
 
 
858
 
                case PROP_X_LABEL:
859
 
        case PROP_LABEL:
860
 
          g_value_set_string (value, priv->label);
861
 
          break;
862
 
 
863
 
        case PROP_X_LABEL_GUIDE:
864
 
        case PROP_LABEL_GUIDE:
865
 
          g_value_set_string (value, priv->label_guide);
866
 
          break;
867
 
 
868
 
                case PROP_X_ORDERING_INDEX:
869
 
                case PROP_ORDERING_INDEX:
870
 
                  g_value_set_uint(value, priv->ordering_index);
871
 
                  break;
872
 
 
873
 
        default:
874
 
          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
875
 
          break;
876
 
        }
877
 
 
878
 
        return;
879
 
}
880
 
 
881
 
/* Sends the label changed signal and resets the source ID */
882
 
static gboolean
883
 
signal_label_change_idle (gpointer user_data)
884
 
{
885
 
        AppIndicator * self = (AppIndicator *)user_data;
886
 
        AppIndicatorPrivate *priv = self->priv;
887
 
 
888
 
        g_signal_emit(G_OBJECT(self), signals[NEW_LABEL], 0,
889
 
                      priv->label != NULL ? priv->label : "",
890
 
                      priv->label_guide != NULL ? priv->label_guide : "",
891
 
                      TRUE);
892
 
        g_signal_emit(G_OBJECT(self), signals[X_NEW_LABEL], 0,
893
 
                      priv->label != NULL ? priv->label : "",
894
 
                      priv->label_guide != NULL ? priv->label_guide : "",
895
 
                      TRUE);
896
 
 
897
 
        priv->label_change_idle = 0;
898
 
 
899
 
        return FALSE;
900
 
}
901
 
 
902
 
/* Sets up an idle function to send the label changed signal
903
 
   so that we don't send it too many times. */
904
 
static void
905
 
signal_label_change (AppIndicator * self)
906
 
{
907
 
        AppIndicatorPrivate *priv = self->priv;
908
 
 
909
 
        /* don't set it twice */
910
 
        if (priv->label_change_idle != 0) {
911
 
                return;
912
 
        }
913
 
 
914
 
        priv->label_change_idle = g_idle_add(signal_label_change_idle, self);
915
 
        return;
916
 
}
917
 
 
918
 
/* This function is used to see if we have enough information to
919
 
   connect to things.  If we do, and we're not connected, it
920
 
   connects for us. */
921
 
static void
922
 
check_connect (AppIndicator *self)
923
 
{
924
 
        AppIndicatorPrivate *priv = self->priv;
925
 
 
926
 
        /* We're alreadying connecting or trying to connect. */
927
 
        if (priv->watcher_proxy != NULL) return;
928
 
 
929
 
        /* Do we have enough information? */
930
 
        if (priv->menu == NULL) return;
931
 
        if (priv->icon_name == NULL) return;
932
 
        if (priv->id == NULL) return;
933
 
 
934
 
        gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s", priv->clean_id);
935
 
 
936
 
        dbus_g_connection_register_g_object(priv->connection,
937
 
                                            path,
938
 
                                            G_OBJECT(self));
939
 
 
940
 
        GError * error = NULL;
941
 
        priv->watcher_proxy = dbus_g_proxy_new_for_name_owner(priv->connection,
942
 
                                                              NOTIFICATION_WATCHER_DBUS_ADDR,
943
 
                                                              NOTIFICATION_WATCHER_DBUS_OBJ,
944
 
                                                              NOTIFICATION_WATCHER_DBUS_IFACE,
945
 
                                                              &error);
946
 
        if (error != NULL) {
947
 
                /* Unable to get proxy, but we're handling that now so
948
 
                   it's not a warning anymore. */
949
 
                g_error_free(error);
950
 
                dbus_g_connection_unregister_g_object(priv->connection,
951
 
                                                      G_OBJECT(self));
952
 
                start_fallback_timer(self, FALSE);
953
 
                g_free(path);
954
 
                return;
955
 
        }
956
 
 
957
 
        g_signal_connect(G_OBJECT(priv->watcher_proxy), "destroy", G_CALLBACK(watcher_proxy_destroyed), self);
958
 
        org_kde_StatusNotifierWatcher_register_status_notifier_item_async(priv->watcher_proxy, path, register_service_cb, self);
959
 
        g_free(path);
960
 
 
961
 
        /* Emit the AppIndicator::connection-changed signal*/
962
 
    g_signal_emit (self, signals[CONNECTION_CHANGED], 0, TRUE);
963
 
 
964
 
        return;
965
 
}
966
 
 
967
 
/* A function that gets called when the watcher dies.  Like
968
 
   dies dies.  Not our friend anymore. */
969
 
static void
970
 
watcher_proxy_destroyed (GObject * object, gpointer data)
971
 
{
972
 
        AppIndicator * self = APP_INDICATOR(data);
973
 
        g_return_if_fail(self != NULL);
974
 
 
975
 
        dbus_g_connection_unregister_g_object(self->priv->connection,
976
 
                                              G_OBJECT(self));
977
 
        self->priv->watcher_proxy = NULL;
978
 
 
979
 
    /* Emit the AppIndicator::connection-changed signal*/
980
 
    g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
981
 
        
982
 
        start_fallback_timer(self, FALSE);
983
 
        return;
984
 
}
985
 
 
986
 
/* Responce from the DBus command to register a service
987
 
   with a NotificationWatcher. */
988
 
static void
989
 
register_service_cb (DBusGProxy * proxy, GError * error, gpointer data)
990
 
{
991
 
        g_return_if_fail(IS_APP_INDICATOR(data));
992
 
        AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
993
 
 
994
 
        if (error != NULL) {
995
 
                /* They didn't respond, ewww.  Not sure what they could
996
 
                   be doing */
997
 
                g_warning("Unable to connect to the Notification Watcher: %s", error->message);
998
 
                dbus_g_connection_unregister_g_object(priv->connection,
999
 
                                                      G_OBJECT(data));
1000
 
                g_object_unref(G_OBJECT(priv->watcher_proxy));
1001
 
                priv->watcher_proxy = NULL;
1002
 
                start_fallback_timer(APP_INDICATOR(data), TRUE);
1003
 
        }
1004
 
 
1005
 
        if (priv->status_icon) {
1006
 
                AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data);
1007
 
                if (class->unfallback != NULL) {
1008
 
                        class->unfallback(APP_INDICATOR(data), priv->status_icon);
1009
 
                        priv->status_icon = NULL;
1010
 
                } 
1011
 
        }
1012
 
 
1013
 
        return;
1014
 
}
1015
 
 
1016
 
/* A helper function to get the nick out of a given
1017
 
   category enum value. */
1018
 
static const gchar *
1019
 
category_from_enum (AppIndicatorCategory category)
1020
 
{
1021
 
  GEnumValue *value;
1022
 
 
1023
 
  value = g_enum_get_value ((GEnumClass *)g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), category);
1024
 
  return value->value_nick;
1025
 
}
1026
 
 
1027
 
/* Watching the dbus owner change events to see if someone
1028
 
   we care about pops up! */
1029
 
static void
1030
 
dbus_owner_change (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, gpointer data)
1031
 
{
1032
 
        if (new == NULL || new[0] == '\0') {
1033
 
                /* We only care about folks coming on the bus.  Exit quickly otherwise. */
1034
 
                return;
1035
 
        }
1036
 
 
1037
 
        if (g_strcmp0(name, NOTIFICATION_WATCHER_DBUS_ADDR)) {
1038
 
                /* We only care about this address, reject all others. */
1039
 
                return;
1040
 
        }
1041
 
 
1042
 
        /* Woot, there's a new notification watcher in town. */
1043
 
 
1044
 
        AppIndicatorPrivate * priv = APP_INDICATOR_GET_PRIVATE(data);
1045
 
 
1046
 
        if (priv->fallback_timer != 0) {
1047
 
                /* Stop a timer */
1048
 
                g_source_remove(priv->fallback_timer);
1049
 
 
1050
 
                /* Stop listening to bus events */
1051
 
                g_object_unref(G_OBJECT(priv->dbus_proxy));
1052
 
                priv->dbus_proxy = NULL;
1053
 
        }
1054
 
 
1055
 
        /* Let's start from the very beginning */
1056
 
        check_connect(APP_INDICATOR(data));
1057
 
 
1058
 
        return;
1059
 
}
1060
 
 
1061
 
/* Checking to see if someone already has the name we're looking for */
1062
 
static void
1063
 
check_owner_cb (DBusGProxy *proxy, gboolean exists, GError *error, gpointer userdata)
1064
 
{
1065
 
        if (error != NULL) {
1066
 
                g_warning("Unable to check for '" NOTIFICATION_WATCHER_DBUS_ADDR "' on DBus.  No worries, but concerning.");
1067
 
                return;
1068
 
        }
1069
 
 
1070
 
        if (exists) {
1071
 
                g_debug("Woah, we actually has a race condition with dbus");
1072
 
                dbus_owner_change(proxy, NOTIFICATION_WATCHER_DBUS_ADDR, NULL, "Non NULL", userdata);
1073
 
        }
1074
 
 
1075
 
        return;
1076
 
}
1077
 
 
1078
 
/* This is an idle function to create the proxy.  This is mostly
1079
 
   because start_fallback_timer can get called in the distruction
1080
 
   of a proxy and thus the proxy manager gets confused when creating
1081
 
   a new proxy as part of destroying an old one.  This function being
1082
 
   on idle means that we'll just do it outside of the same stack where
1083
 
   the previous proxy is being destroyed. */
1084
 
static gboolean
1085
 
setup_name_owner_proxy (gpointer data)
1086
 
{
1087
 
        g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
1088
 
        AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
1089
 
 
1090
 
        if (priv->dbus_proxy == NULL) {
1091
 
                priv->dbus_proxy = dbus_g_proxy_new_for_name(priv->connection,
1092
 
                                                             DBUS_SERVICE_DBUS,
1093
 
                                                             DBUS_PATH_DBUS,
1094
 
                                                             DBUS_INTERFACE_DBUS);
1095
 
                dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged",
1096
 
                                        G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
1097
 
                                        G_TYPE_INVALID);
1098
 
                dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
1099
 
                                            G_CALLBACK(dbus_owner_change), data, NULL);
1100
 
 
1101
 
                /* Check to see if anyone has the name we're looking for
1102
 
                   just incase we missed it changing. */
1103
 
 
1104
 
                org_freedesktop_DBus_name_has_owner_async(priv->dbus_proxy, NOTIFICATION_WATCHER_DBUS_ADDR, check_owner_cb, data);
1105
 
        }
1106
 
 
1107
 
        return FALSE;
1108
 
}
1109
 
 
1110
 
/* A function that will start the fallback timer if it's not
1111
 
   already started.  It sets up the DBus watcher to see if
1112
 
   there is a change.  Also, provides an override mode for cases
1113
 
   where it's unlikely that a timer will help anything. */
1114
 
static void
1115
 
start_fallback_timer (AppIndicator * self, gboolean disable_timeout)
1116
 
{
1117
 
        g_return_if_fail(IS_APP_INDICATOR(self));
1118
 
        AppIndicatorPrivate * priv = APP_INDICATOR(self)->priv;
1119
 
 
1120
 
        if (priv->fallback_timer != 0) {
1121
 
                /* The timer is set, let's just be happy with the one
1122
 
                   we've already got running */
1123
 
                return;
1124
 
        }
1125
 
 
1126
 
        if (priv->status_icon != NULL) {
1127
 
                /* We're already fallen back.  Let's not do it again. */
1128
 
                return;
1129
 
        }
1130
 
 
1131
 
        if (priv->dbus_proxy == NULL) {
1132
 
                /* NOTE: Read the comment on setup_name_owner_proxy */
1133
 
                g_idle_add(setup_name_owner_proxy, self);
1134
 
        }
1135
 
 
1136
 
        if (disable_timeout) {
1137
 
                fallback_timer_expire(self);
1138
 
        } else {
1139
 
                priv->fallback_timer = g_timeout_add(DEFAULT_FALLBACK_TIMER, fallback_timer_expire, self);
1140
 
        }
1141
 
 
1142
 
        return;
1143
 
}
1144
 
 
1145
 
/* A function that gets executed when we want to change the
1146
 
   state of the fallback. */
1147
 
static gboolean
1148
 
fallback_timer_expire (gpointer data)
1149
 
{
1150
 
        g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
1151
 
 
1152
 
        AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
1153
 
        AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data);
1154
 
 
1155
 
        if (priv->status_icon == NULL) {
1156
 
                if (class->fallback != NULL) {
1157
 
                        priv->status_icon = class->fallback(APP_INDICATOR(data));
1158
 
                } 
1159
 
        } else {
1160
 
                if (class->unfallback != NULL) {
1161
 
                        class->unfallback(APP_INDICATOR(data), priv->status_icon);
1162
 
                        priv->status_icon = NULL;
1163
 
                } else {
1164
 
                        g_warning("No 'unfallback' function but the 'fallback' function returned a non-NULL result.");
1165
 
                }
1166
 
        }
1167
 
 
1168
 
        priv->fallback_timer = 0;
1169
 
        return FALSE;
1170
 
}
1171
 
 
1172
 
/* emit a NEW_ICON signal in response for the theme change */
1173
 
static void
1174
 
theme_changed_cb (GtkIconTheme * theme, gpointer user_data)
1175
 
{
1176
 
        g_signal_emit (user_data, signals[NEW_ICON], 0, TRUE);
1177
 
}
1178
 
 
1179
 
/* Creates a StatusIcon that can be used when the application
1180
 
   indicator area isn't available. */
1181
 
static GtkStatusIcon *
1182
 
fallback (AppIndicator * self)
1183
 
{
1184
 
        GtkStatusIcon * icon = gtk_status_icon_new();
1185
 
 
1186
 
        gtk_status_icon_set_title(icon, app_indicator_get_id(self));
1187
 
        
1188
 
        g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_STATUS,
1189
 
                G_CALLBACK(status_icon_status_wrapper), icon);
1190
 
        g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ICON,
1191
 
                G_CALLBACK(status_icon_changes), icon);
1192
 
        g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON,
1193
 
                G_CALLBACK(status_icon_changes), icon);
1194
 
 
1195
 
        status_icon_changes(self, icon);
1196
 
 
1197
 
        g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self);
1198
 
 
1199
 
        return icon;
1200
 
}
1201
 
 
1202
 
/* A wrapper as the status update prototype is a little
1203
 
   bit different, but we want to handle it the same. */
1204
 
static void
1205
 
status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data)
1206
 
{
1207
 
        return status_icon_changes(self, data);
1208
 
}
1209
 
 
1210
 
/* This tracks changes to either the status or the icons
1211
 
   that are associated with the app indicator */
1212
 
static void
1213
 
status_icon_changes (AppIndicator * self, gpointer data)
1214
 
{
1215
 
        GtkStatusIcon * icon = GTK_STATUS_ICON(data);
1216
 
        GIcon *themed_icon = NULL;
1217
 
        gchar *longname = NULL;
1218
 
 
1219
 
        switch (app_indicator_get_status(self)) {
1220
 
        case APP_INDICATOR_STATUS_PASSIVE:
1221
 
                longname = append_panel_icon_suffix(app_indicator_get_icon(self));
1222
 
                themed_icon = g_themed_icon_new_with_default_fallbacks (longname);
1223
 
                gtk_status_icon_set_visible(icon, FALSE);
1224
 
                gtk_status_icon_set_from_gicon(icon, themed_icon);
1225
 
                break;
1226
 
        case APP_INDICATOR_STATUS_ACTIVE:
1227
 
                longname = append_panel_icon_suffix(app_indicator_get_icon(self));
1228
 
                themed_icon = g_themed_icon_new_with_default_fallbacks (longname);
1229
 
                gtk_status_icon_set_from_gicon(icon, themed_icon);
1230
 
                gtk_status_icon_set_visible(icon, TRUE);
1231
 
                break;
1232
 
        case APP_INDICATOR_STATUS_ATTENTION:
1233
 
                longname = append_panel_icon_suffix(app_indicator_get_attention_icon(self));
1234
 
                themed_icon = g_themed_icon_new_with_default_fallbacks (longname);
1235
 
                gtk_status_icon_set_from_gicon(icon, themed_icon);
1236
 
                gtk_status_icon_set_visible(icon, TRUE);
1237
 
                break;
1238
 
        };
1239
 
 
1240
 
        if (themed_icon) {
1241
 
                g_object_unref (themed_icon);
1242
 
        }
1243
 
 
1244
 
        if (longname) {
1245
 
                g_free(longname);
1246
 
        }
1247
 
 
1248
 
        return;
1249
 
}
1250
 
 
1251
 
/* Handles the activate action by the status icon by showing
1252
 
   the menu in a popup. */
1253
 
static void
1254
 
status_icon_activate (GtkStatusIcon * icon, gpointer data)
1255
 
{
1256
 
        GtkMenu * menu = app_indicator_get_menu(APP_INDICATOR(data));
1257
 
        if (menu == NULL)
1258
 
                return;
1259
 
        
1260
 
        gtk_menu_popup(menu,
1261
 
                       NULL, /* Parent Menu */
1262
 
                       NULL, /* Parent item */
1263
 
                       gtk_status_icon_position_menu,
1264
 
                       icon,
1265
 
                       1, /* Button */
1266
 
                       gtk_get_current_event_time());
1267
 
 
1268
 
        return;
1269
 
}
1270
 
 
1271
 
/* Removes the status icon as the application indicator area
1272
 
   is now up and running again. */
1273
 
static void
1274
 
unfallback (AppIndicator * self, GtkStatusIcon * status_icon)
1275
 
{
1276
 
        g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_status_wrapper, status_icon);
1277
 
        g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_changes, status_icon);
1278
 
        gtk_status_icon_set_visible(status_icon, FALSE);
1279
 
        g_object_unref(G_OBJECT(status_icon));
1280
 
        return;
1281
 
}
1282
 
 
1283
 
/* A helper function that appends PANEL_ICON_SUFFIX to the given icon name
1284
 
   if it's missing. */
1285
 
static gchar *
1286
 
append_panel_icon_suffix (const gchar *icon_name)
1287
 
{
1288
 
        gchar * long_name = NULL;
1289
 
 
1290
 
        if (!g_str_has_suffix (icon_name, PANEL_ICON_SUFFIX)) {
1291
 
                long_name =
1292
 
                    g_strdup_printf("%s-%s", icon_name, PANEL_ICON_SUFFIX);
1293
 
        } else {
1294
 
                long_name = g_strdup (icon_name);
1295
 
        }
1296
 
 
1297
 
        return long_name;       
1298
 
}
1299
 
 
1300
 
 
1301
 
/* ************************* */
1302
 
/*    Public Functions       */
1303
 
/* ************************* */
1304
 
 
1305
 
/**
1306
 
        app_indicator_new:
1307
 
        @id: The unique id of the indicator to create.
1308
 
        @icon_name: The icon name for this indicator
1309
 
        @category: The category of indicator.
1310
 
 
1311
 
                Creates a new #AppIndicator setting the properties:
1312
 
                #AppIndicator:id with @id, #AppIndicator:category
1313
 
                with @category and #AppIndicator:icon-name with
1314
 
                @icon_name.
1315
 
 
1316
 
        Return value: A pointer to a new #AppIndicator object.
1317
 
 */
1318
 
AppIndicator *
1319
 
app_indicator_new (const gchar          *id,
1320
 
                   const gchar          *icon_name,
1321
 
                   AppIndicatorCategory  category)
1322
 
{
1323
 
  AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
1324
 
                                          PROP_ID_S, id,
1325
 
                                          PROP_CATEGORY_S, category_from_enum (category),
1326
 
                                          PROP_ICON_NAME_S, icon_name,
1327
 
                                          NULL);
1328
 
 
1329
 
  return indicator;
1330
 
}
1331
 
 
1332
 
/**
1333
 
        app_indicator_new_with_path:
1334
 
        @id: The unique id of the indicator to create.
1335
 
        @icon_name: The icon name for this indicator
1336
 
        @category: The category of indicator.
1337
 
        @icon_theme_path: A custom path for finding icons.
1338
 
 
1339
 
                Creates a new #AppIndicator setting the properties:
1340
 
                #AppIndicator:id with @id, #AppIndicator:category
1341
 
                with @category, #AppIndicator:icon-name with
1342
 
                @icon_name and #AppIndicator:icon-theme-path with @icon_theme_path.
1343
 
 
1344
 
        Return value: A pointer to a new #AppIndicator object.
1345
 
 */
1346
 
AppIndicator *
1347
 
app_indicator_new_with_path (const gchar          *id,
1348
 
                             const gchar          *icon_name,
1349
 
                             AppIndicatorCategory  category,
1350
 
                             const gchar          *icon_theme_path)
1351
 
{
1352
 
        AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
1353
 
                                                PROP_ID_S, id,
1354
 
                                                PROP_CATEGORY_S, category_from_enum (category),
1355
 
                                                PROP_ICON_NAME_S, icon_name,
1356
 
                                                PROP_ICON_THEME_PATH_S, icon_theme_path,
1357
 
                                                NULL);
1358
 
 
1359
 
        return indicator;
1360
 
}
1361
 
 
1362
 
/**
1363
 
        app_indicator_get_type:
1364
 
 
1365
 
        Generates or returns the unique #GType for #AppIndicator.
1366
 
 
1367
 
        Return value: A unique #GType for #AppIndicator objects.
1368
 
*/
1369
 
 
1370
 
/**
1371
 
        app_indicator_set_status:
1372
 
        @self: The #AppIndicator object to use
1373
 
        @status: The status to set for this indicator
1374
 
 
1375
 
        Wrapper function for property #AppIndicator:status.
1376
 
*/
1377
 
void
1378
 
app_indicator_set_status (AppIndicator *self, AppIndicatorStatus status)
1379
 
{
1380
 
  g_return_if_fail (IS_APP_INDICATOR (self));
1381
 
 
1382
 
  if (self->priv->status != status)
1383
 
    {
1384
 
      GEnumValue *value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), status);
1385
 
 
1386
 
      self->priv->status = status;
1387
 
      g_signal_emit (self, signals[NEW_STATUS], 0, value->value_nick);
1388
 
    }
1389
 
}
1390
 
 
1391
 
/**
1392
 
        app_indicator_set_attention_icon:
1393
 
        @self: The #AppIndicator object to use
1394
 
        @icon_name: The name of the attention icon to set for this indicator
1395
 
 
1396
 
        Wrapper function for property #AppIndicator:attention-icon-name.
1397
 
*/
1398
 
void
1399
 
app_indicator_set_attention_icon (AppIndicator *self, const gchar *icon_name)
1400
 
{
1401
 
  g_return_if_fail (IS_APP_INDICATOR (self));
1402
 
  g_return_if_fail (icon_name != NULL);
1403
 
 
1404
 
  if (g_strcmp0 (self->priv->attention_icon_name, icon_name) != 0)
1405
 
    {
1406
 
      if (self->priv->attention_icon_name)
1407
 
        g_free (self->priv->attention_icon_name);
1408
 
 
1409
 
      self->priv->attention_icon_name = g_strdup(icon_name);
1410
 
 
1411
 
      g_signal_emit (self, signals[NEW_ATTENTION_ICON], 0, TRUE);
1412
 
    }
1413
 
 
1414
 
  return;
1415
 
}
1416
 
 
1417
 
/**
1418
 
        app_indicator_set_icon:
1419
 
        @self: The #AppIndicator object to use
1420
 
        @icon_name: The icon name to set.
1421
 
 
1422
 
                Sets the default icon to use when the status is active but
1423
 
                not set to attention.  In most cases, this should be the
1424
 
                application icon for the program.
1425
 
                Wrapper function for property #AppIndicator:icon-name.
1426
 
**/
1427
 
void
1428
 
app_indicator_set_icon (AppIndicator *self, const gchar *icon_name)
1429
 
{
1430
 
  g_return_if_fail (IS_APP_INDICATOR (self));
1431
 
  g_return_if_fail (icon_name != NULL);
1432
 
 
1433
 
  if (g_strcmp0 (self->priv->icon_name, icon_name) != 0)
1434
 
    {
1435
 
      if (self->priv->icon_name)
1436
 
        g_free (self->priv->icon_name);
1437
 
 
1438
 
      self->priv->icon_name = g_strdup(icon_name);
1439
 
 
1440
 
      g_signal_emit (self, signals[NEW_ICON], 0, TRUE);
1441
 
    }
1442
 
 
1443
 
  return;
1444
 
}
1445
 
 
1446
 
/**
1447
 
        app_indicator_set_label:
1448
 
        @self: The #AppIndicator object to use
1449
 
        @label: The label to show next to the icon.
1450
 
        @guide: A guide to size the label correctly.
1451
 
 
1452
 
        This is a wrapper function for the #AppIndicator:label and
1453
 
        #AppIndicator:guide properties.  This function can take #NULL
1454
 
        as either @label or @guide and will clear the entries.
1455
 
*/
1456
 
void
1457
 
app_indicator_set_label (AppIndicator *self, const gchar * label, const gchar * guide)
1458
 
{
1459
 
        g_return_if_fail (IS_APP_INDICATOR (self));
1460
 
        /* Note: The label can be NULL, it's okay */
1461
 
        /* Note: The guide can be NULL, it's okay */
1462
 
 
1463
 
        g_object_set(G_OBJECT(self),
1464
 
                     PROP_LABEL_S,       label == NULL ? "" : label,
1465
 
                     PROP_LABEL_GUIDE_S, guide == NULL ? "" : guide,
1466
 
                     NULL);
1467
 
 
1468
 
        return;
1469
 
}
1470
 
 
1471
 
/**
1472
 
        app_indicator_set_icon_theme_path:
1473
 
        @self: The #AppIndicator object to use
1474
 
        @icon_theme_path: The icon theme path to set.
1475
 
 
1476
 
                Sets the path to use when searching for icons.
1477
 
**/
1478
 
void
1479
 
app_indicator_set_icon_theme_path (AppIndicator *self, const gchar *icon_theme_path)
1480
 
{
1481
 
  g_return_if_fail (IS_APP_INDICATOR (self));
1482
 
 
1483
 
  if (g_strcmp0 (self->priv->icon_theme_path, icon_theme_path) != 0)
1484
 
    {
1485
 
      if (self->priv->icon_theme_path != NULL)
1486
 
            g_free(self->priv->icon_theme_path);
1487
 
 
1488
 
      self->priv->icon_theme_path = g_strdup(icon_theme_path);
1489
 
 
1490
 
      g_signal_emit (self, signals[NEW_ICON_THEME_PATH], 0, g_strdup(self->priv->icon_theme_path));
1491
 
    }
1492
 
 
1493
 
  return;
1494
 
}
1495
 
 
1496
 
static void
1497
 
activate_menuitem (DbusmenuMenuitem *mi, guint timestamp, gpointer user_data)
1498
 
{
1499
 
  GtkWidget *widget = (GtkWidget *)user_data;
1500
 
 
1501
 
  gtk_menu_item_activate (GTK_MENU_ITEM (widget));
1502
 
}
1503
 
 
1504
 
static void
1505
 
widget_toggled (GtkWidget *widget, DbusmenuMenuitem *mi)
1506
 
{
1507
 
  dbusmenu_menuitem_property_set_int (mi,
1508
 
                                      DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
1509
 
                                      gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
1510
 
}
1511
 
 
1512
 
static void
1513
 
menuitem_iterate (GtkWidget *widget,
1514
 
                  gpointer   data)
1515
 
{
1516
 
  if (GTK_IS_LABEL (widget))
1517
 
    {
1518
 
      DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
1519
 
 
1520
 
      dbusmenu_menuitem_property_set (child,
1521
 
                                      DBUSMENU_MENUITEM_PROP_LABEL,
1522
 
                                      gtk_label_get_text (GTK_LABEL (widget)));
1523
 
    }
1524
 
}
1525
 
 
1526
 
static gboolean
1527
 
should_show_image (GtkImage *image)
1528
 
{
1529
 
  GtkWidget *item;
1530
 
 
1531
 
  item = gtk_widget_get_ancestor (GTK_WIDGET (image),
1532
 
                                  GTK_TYPE_IMAGE_MENU_ITEM);
1533
 
 
1534
 
  if (item)
1535
 
    {
1536
 
      GtkSettings *settings;
1537
 
      gboolean gtk_menu_images;
1538
 
 
1539
 
      settings = gtk_widget_get_settings (item);
1540
 
 
1541
 
      g_object_get (settings, "gtk-menu-images", &gtk_menu_images, NULL);
1542
 
 
1543
 
      if (gtk_menu_images)
1544
 
        return TRUE;
1545
 
 
1546
 
      return gtk_image_menu_item_get_always_show_image (GTK_IMAGE_MENU_ITEM (item));
1547
 
    }
1548
 
 
1549
 
  return FALSE;
1550
 
}
1551
 
 
1552
 
static void
1553
 
update_icon_name (DbusmenuMenuitem *menuitem,
1554
 
                  GtkImage         *image)
1555
 
{
1556
 
  const gchar *icon_name = NULL;
1557
 
 
1558
 
  if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME)
1559
 
    return;
1560
 
 
1561
 
  gtk_image_get_icon_name (image, &icon_name, NULL);
1562
 
 
1563
 
  if (should_show_image (image))
1564
 
    dbusmenu_menuitem_property_set (menuitem,
1565
 
                                    DBUSMENU_MENUITEM_PROP_ICON_NAME,
1566
 
                                    icon_name);
1567
 
  else
1568
 
    dbusmenu_menuitem_property_remove (menuitem,
1569
 
                                       DBUSMENU_MENUITEM_PROP_ICON_NAME);
1570
 
}
1571
 
 
1572
 
/* return value specifies whether the label is set or not */
1573
 
static gboolean
1574
 
update_stock_item (DbusmenuMenuitem *menuitem,
1575
 
                   GtkImage         *image)
1576
 
{
1577
 
  GtkStockItem stock;
1578
 
  gchar *stock_id = NULL;
1579
 
 
1580
 
  if (gtk_image_get_storage_type (image) != GTK_IMAGE_STOCK)
1581
 
    return FALSE;
1582
 
 
1583
 
  gtk_image_get_stock (image, &stock_id, NULL);
1584
 
  gtk_stock_lookup (stock_id, &stock);
1585
 
 
1586
 
  if (should_show_image (image))
1587
 
    dbusmenu_menuitem_property_set (menuitem,
1588
 
                                    DBUSMENU_MENUITEM_PROP_ICON_NAME,
1589
 
                                    stock_id);
1590
 
  else
1591
 
    dbusmenu_menuitem_property_remove (menuitem,
1592
 
                                       DBUSMENU_MENUITEM_PROP_ICON_NAME);
1593
 
 
1594
 
  const gchar * label = dbusmenu_menuitem_property_get (menuitem,
1595
 
                                  DBUSMENU_MENUITEM_PROP_LABEL);
1596
 
  
1597
 
  if (stock.label != NULL && label != NULL)
1598
 
    {
1599
 
      dbusmenu_menuitem_property_set (menuitem,
1600
 
                                      DBUSMENU_MENUITEM_PROP_LABEL,
1601
 
                                      stock.label);
1602
 
 
1603
 
      return TRUE;
1604
 
    }
1605
 
 
1606
 
  return FALSE;
1607
 
}
1608
 
 
1609
 
static void
1610
 
image_notify_cb (GtkWidget  *widget,
1611
 
                 GParamSpec *pspec,
1612
 
                 gpointer    data)
1613
 
{
1614
 
  DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
1615
 
  GtkImage *image = GTK_IMAGE (widget);
1616
 
 
1617
 
  if (pspec->name == g_intern_static_string ("stock"))
1618
 
    {
1619
 
      update_stock_item (child, image);
1620
 
    }
1621
 
  else if (pspec->name == g_intern_static_string ("icon-name"))
1622
 
    {
1623
 
      update_icon_name (child, image);
1624
 
    }
1625
 
}
1626
 
 
1627
 
static void
1628
 
widget_notify_cb (GtkWidget  *widget,
1629
 
                  GParamSpec *pspec,
1630
 
                  gpointer    data)
1631
 
{
1632
 
  DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
1633
 
 
1634
 
  if (pspec->name == g_intern_static_string ("sensitive"))
1635
 
    {
1636
 
      dbusmenu_menuitem_property_set_bool (child,
1637
 
                                           DBUSMENU_MENUITEM_PROP_ENABLED,
1638
 
                                           gtk_widget_is_sensitive (widget));
1639
 
    }
1640
 
  else if (pspec->name == g_intern_static_string ("label"))
1641
 
    {
1642
 
      dbusmenu_menuitem_property_set (child,
1643
 
                                      DBUSMENU_MENUITEM_PROP_LABEL,
1644
 
                                      gtk_menu_item_get_label (GTK_MENU_ITEM (widget)));
1645
 
    }
1646
 
  else if (pspec->name == g_intern_static_string ("visible"))
1647
 
    {
1648
 
      dbusmenu_menuitem_property_set_bool (child,
1649
 
                                           DBUSMENU_MENUITEM_PROP_VISIBLE,
1650
 
                                           gtk_widget_get_visible (widget));
1651
 
    }
1652
 
}
1653
 
 
1654
 
static void
1655
 
action_notify_cb (GtkAction  *action,
1656
 
                  GParamSpec *pspec,
1657
 
                  gpointer    data)
1658
 
{
1659
 
  DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
1660
 
 
1661
 
  if (pspec->name == g_intern_static_string ("active"))
1662
 
    {
1663
 
      dbusmenu_menuitem_property_set_bool (child,
1664
 
                                      DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
1665
 
                                      gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
1666
 
    }
1667
 
    
1668
 
  if (pspec->name == g_intern_static_string ("label"))
1669
 
    {
1670
 
      dbusmenu_menuitem_property_set (child,
1671
 
                                      DBUSMENU_MENUITEM_PROP_LABEL,
1672
 
                                      gtk_action_get_label (action));
1673
 
    }
1674
 
}
1675
 
 
1676
 
static void
1677
 
container_iterate (GtkWidget *widget,
1678
 
                   gpointer   data)
1679
 
{
1680
 
  DbusmenuMenuitem *root = (DbusmenuMenuitem *)data;
1681
 
  DbusmenuMenuitem *child;
1682
 
  GtkWidget *submenu = NULL;
1683
 
  const gchar *label = NULL;
1684
 
  gboolean label_set = FALSE;
1685
 
 
1686
 
  if (GTK_IS_TEAROFF_MENU_ITEM(widget)) {
1687
 
        return;
1688
 
  }
1689
 
 
1690
 
  child = dbusmenu_menuitem_new ();
1691
 
 
1692
 
  if (GTK_IS_SEPARATOR_MENU_ITEM (widget))
1693
 
    {
1694
 
      dbusmenu_menuitem_property_set (child,
1695
 
                                      "type",
1696
 
                                      DBUSMENU_CLIENT_TYPES_SEPARATOR);
1697
 
    }
1698
 
  else
1699
 
    {
1700
 
      if (GTK_IS_CHECK_MENU_ITEM (widget))
1701
 
        {
1702
 
          GtkCheckMenuItem *check;
1703
 
 
1704
 
          check = GTK_CHECK_MENU_ITEM (widget);
1705
 
          label = gtk_menu_item_get_label (GTK_MENU_ITEM (widget));
1706
 
 
1707
 
          dbusmenu_menuitem_property_set (child,
1708
 
                                          DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
1709
 
                                          GTK_IS_RADIO_MENU_ITEM (widget) ? DBUSMENU_MENUITEM_TOGGLE_RADIO : DBUSMENU_MENUITEM_TOGGLE_CHECK);
1710
 
 
1711
 
          dbusmenu_menuitem_property_set (child,
1712
 
                                          DBUSMENU_MENUITEM_PROP_LABEL,
1713
 
                                          label);
1714
 
 
1715
 
          label_set = TRUE;
1716
 
 
1717
 
          dbusmenu_menuitem_property_set_int (child,
1718
 
                                              DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
1719
 
                                              gtk_check_menu_item_get_active (check) ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
1720
 
 
1721
 
          g_signal_connect (widget,
1722
 
                            "toggled",
1723
 
                            G_CALLBACK (widget_toggled),
1724
 
                            child);
1725
 
        }
1726
 
      else if (GTK_IS_IMAGE_MENU_ITEM (widget))
1727
 
        {
1728
 
          GtkWidget *image;
1729
 
          GtkImageType image_type;
1730
 
 
1731
 
          image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (widget));
1732
 
          image_type = gtk_image_get_storage_type (GTK_IMAGE (image));
1733
 
 
1734
 
          g_signal_connect (image,
1735
 
                            "notify",
1736
 
                            G_CALLBACK (image_notify_cb),
1737
 
                            child);
1738
 
 
1739
 
          if (image_type == GTK_IMAGE_STOCK)
1740
 
            {
1741
 
              label_set = update_stock_item (child, GTK_IMAGE (image));
1742
 
            }
1743
 
          else if (image_type == GTK_IMAGE_ICON_NAME)
1744
 
            {
1745
 
              update_icon_name (child, GTK_IMAGE (image));
1746
 
            }
1747
 
        }
1748
 
    }
1749
 
 
1750
 
  if (!label_set)
1751
 
    {
1752
 
      if (label != NULL)
1753
 
        {
1754
 
          dbusmenu_menuitem_property_set (child,
1755
 
                                          DBUSMENU_MENUITEM_PROP_LABEL,
1756
 
                                          label);
1757
 
        }
1758
 
      else
1759
 
        {
1760
 
          /* find label child widget */
1761
 
          gtk_container_forall (GTK_CONTAINER (widget),
1762
 
                                menuitem_iterate,
1763
 
                                child);
1764
 
        }
1765
 
    }
1766
 
 
1767
 
  if (GTK_IS_MENU_ITEM (widget))
1768
 
    {
1769
 
      submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
1770
 
      if (submenu != NULL)
1771
 
        {
1772
 
          gtk_container_foreach (GTK_CONTAINER (submenu),
1773
 
                                container_iterate,
1774
 
                                child);
1775
 
          g_signal_connect_object (submenu,
1776
 
                                   "child-added",
1777
 
                                   G_CALLBACK (submenu_changed),
1778
 
                                   child,
1779
 
                                   0);
1780
 
          g_signal_connect_object (submenu,
1781
 
                                   "child-removed",
1782
 
                                   G_CALLBACK (submenu_changed),
1783
 
                                   child,
1784
 
                                   0);
1785
 
        }
1786
 
    }
1787
 
 
1788
 
  dbusmenu_menuitem_property_set_bool (child,
1789
 
                                       DBUSMENU_MENUITEM_PROP_ENABLED,
1790
 
                                       gtk_widget_is_sensitive (widget));
1791
 
  dbusmenu_menuitem_property_set_bool (child,
1792
 
                                       DBUSMENU_MENUITEM_PROP_VISIBLE,
1793
 
                                       gtk_widget_get_visible (widget));
1794
 
 
1795
 
  g_signal_connect (widget, "notify",
1796
 
                    G_CALLBACK (widget_notify_cb), child);
1797
 
 
1798
 
  if (GTK_IS_ACTIVATABLE (widget))
1799
 
    {
1800
 
      GtkActivatable *activatable = GTK_ACTIVATABLE (widget);
1801
 
 
1802
 
      if (gtk_activatable_get_use_action_appearance (activatable))
1803
 
        {
1804
 
          GtkAction *action = gtk_activatable_get_related_action (activatable);
1805
 
 
1806
 
          if (action)
1807
 
            {
1808
 
              g_signal_connect_object (action, "notify",
1809
 
                                       G_CALLBACK (action_notify_cb),
1810
 
                                       child,
1811
 
                                       G_CONNECT_AFTER);
1812
 
            }
1813
 
        }
1814
 
    }
1815
 
 
1816
 
  g_signal_connect (G_OBJECT (child),
1817
 
                    DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
1818
 
                    G_CALLBACK (activate_menuitem), widget);
1819
 
  dbusmenu_menuitem_child_append (root, child);
1820
 
 
1821
 
  /* Get rid of initial ref now that the root is
1822
 
     holding the object */
1823
 
  g_object_unref(child);
1824
 
 
1825
 
  return;
1826
 
}
1827
 
 
1828
 
static void
1829
 
submenu_changed (GtkWidget *widget,
1830
 
                 GtkWidget *child,
1831
 
                 gpointer   data)
1832
 
{
1833
 
  DbusmenuMenuitem *root = (DbusmenuMenuitem *)data;
1834
 
  GList *children, *l;
1835
 
  children = dbusmenu_menuitem_get_children (root);
1836
 
 
1837
 
  for (l = children; l;)
1838
 
    {
1839
 
      DbusmenuMenuitem *c = (DbusmenuMenuitem *)l->data;
1840
 
      l = l->next;
1841
 
      dbusmenu_menuitem_child_delete (root, c);
1842
 
    }
1843
 
 
1844
 
  gtk_container_foreach (GTK_CONTAINER (widget),
1845
 
                        container_iterate,
1846
 
                        root);
1847
 
}
1848
 
 
1849
 
static void
1850
 
setup_dbusmenu (AppIndicator *self)
1851
 
{
1852
 
  AppIndicatorPrivate *priv;
1853
 
  DbusmenuMenuitem *root;
1854
 
 
1855
 
  priv = self->priv;
1856
 
  root = dbusmenu_menuitem_new ();
1857
 
 
1858
 
  if (priv->menu)
1859
 
    {
1860
 
      gtk_container_foreach (GTK_CONTAINER (priv->menu),
1861
 
                            container_iterate,
1862
 
                            root);
1863
 
    }
1864
 
 
1865
 
  if (priv->menuservice == NULL)
1866
 
    {
1867
 
      gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s/Menu", priv->clean_id);
1868
 
      priv->menuservice = dbusmenu_server_new (path);
1869
 
      g_free(path);
1870
 
    }
1871
 
 
1872
 
  dbusmenu_server_set_root (priv->menuservice, root);
1873
 
 
1874
 
  return;
1875
 
}
1876
 
 
1877
 
static void
1878
 
client_menu_changed (GtkWidget    *widget,
1879
 
                     GtkWidget    *child,
1880
 
                     AppIndicator *indicator)
1881
 
{
1882
 
  setup_dbusmenu (indicator);
1883
 
}
1884
 
 
1885
 
/**
1886
 
        app_indicator_set_menu:
1887
 
        @self: The #AppIndicator
1888
 
        @menu: A #GtkMenu to set
1889
 
 
1890
 
        Sets the menu that should be shown when the Application Indicator
1891
 
        is clicked on in the panel.  An application indicator will not
1892
 
        be rendered unless it has a menu.
1893
 
        
1894
 
        Wrapper function for property #AppIndicator:menu.
1895
 
**/
1896
 
void
1897
 
app_indicator_set_menu (AppIndicator *self, GtkMenu *menu)
1898
 
{
1899
 
  AppIndicatorPrivate *priv;
1900
 
 
1901
 
  g_return_if_fail (IS_APP_INDICATOR (self));
1902
 
  g_return_if_fail (GTK_IS_MENU (menu));
1903
 
  g_return_if_fail (self->priv->clean_id != NULL);
1904
 
 
1905
 
  priv = self->priv;
1906
 
 
1907
 
  if (priv->menu != NULL)
1908
 
    {
1909
 
      g_object_unref (priv->menu);
1910
 
    }
1911
 
 
1912
 
  priv->menu = GTK_WIDGET (menu);
1913
 
  g_object_ref (priv->menu);
1914
 
 
1915
 
  setup_dbusmenu (self);
1916
 
 
1917
 
  check_connect (self);
1918
 
 
1919
 
  g_signal_connect (menu,
1920
 
                    "child-added",
1921
 
                    G_CALLBACK (client_menu_changed),
1922
 
                    self);
1923
 
  g_signal_connect (menu,
1924
 
                    "child-removed",
1925
 
                    G_CALLBACK (client_menu_changed),
1926
 
                    self);
1927
 
}
1928
 
 
1929
 
/**
1930
 
        app_indicator_set_ordering_index:
1931
 
        @self: The #AppIndicator
1932
 
        @ordering_index: A value for the ordering of this app indicator
1933
 
 
1934
 
        Sets the ordering index for the app indicator which effects the
1935
 
        placement of it on the panel.  For almost all app indicator
1936
 
        this is not the function you're looking for.
1937
 
 
1938
 
        Wrapper function for property #AppIndicator:ordering-index.
1939
 
**/
1940
 
void
1941
 
app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index)
1942
 
{
1943
 
        g_return_if_fail (IS_APP_INDICATOR (self));
1944
 
 
1945
 
        self->priv->ordering_index = ordering_index;
1946
 
 
1947
 
        return;
1948
 
}
1949
 
 
1950
 
/**
1951
 
        app_indicator_get_id:
1952
 
        @self: The #AppIndicator object to use
1953
 
 
1954
 
        Wrapper function for property #AppIndicator:id.
1955
 
 
1956
 
        Return value: The current ID
1957
 
*/
1958
 
const gchar *
1959
 
app_indicator_get_id (AppIndicator *self)
1960
 
{
1961
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
1962
 
 
1963
 
  return self->priv->id;
1964
 
}
1965
 
 
1966
 
/**
1967
 
        app_indicator_get_category:
1968
 
        @self: The #AppIndicator object to use
1969
 
 
1970
 
        Wrapper function for property #AppIndicator:category.
1971
 
 
1972
 
        Return value: The current category.
1973
 
*/
1974
 
AppIndicatorCategory
1975
 
app_indicator_get_category (AppIndicator *self)
1976
 
{
1977
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
1978
 
 
1979
 
  return self->priv->category;
1980
 
}
1981
 
 
1982
 
/**
1983
 
        app_indicator_get_status:
1984
 
        @self: The #AppIndicator object to use
1985
 
 
1986
 
        Wrapper function for property #AppIndicator:status.
1987
 
 
1988
 
        Return value: The current status.
1989
 
*/
1990
 
AppIndicatorStatus
1991
 
app_indicator_get_status (AppIndicator *self)
1992
 
{
1993
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), APP_INDICATOR_STATUS_PASSIVE);
1994
 
 
1995
 
  return self->priv->status;
1996
 
}
1997
 
 
1998
 
/**
1999
 
        app_indicator_get_icon:
2000
 
        @self: The #AppIndicator object to use
2001
 
 
2002
 
        Wrapper function for property #AppIndicator:icon-name.
2003
 
 
2004
 
        Return value: The current icon name.
2005
 
*/
2006
 
const gchar *
2007
 
app_indicator_get_icon (AppIndicator *self)
2008
 
{
2009
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2010
 
 
2011
 
  return self->priv->icon_name;
2012
 
}
2013
 
 
2014
 
/**
2015
 
        app_indicator_get_icon_theme_path:
2016
 
        @self: The #AppIndicator object to use
2017
 
 
2018
 
        Wrapper function for property #AppIndicator:icon-theme-path.
2019
 
 
2020
 
        Return value: The current icon theme path.
2021
 
*/
2022
 
const gchar *
2023
 
app_indicator_get_icon_theme_path (AppIndicator *self)
2024
 
{
2025
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2026
 
 
2027
 
  return self->priv->icon_theme_path;
2028
 
}
2029
 
 
2030
 
/**
2031
 
        app_indicator_get_attention_icon:
2032
 
        @self: The #AppIndicator object to use
2033
 
 
2034
 
        Wrapper function for property #AppIndicator:attention-icon-name.
2035
 
 
2036
 
        Return value: The current attention icon name.
2037
 
*/
2038
 
const gchar *
2039
 
app_indicator_get_attention_icon (AppIndicator *self)
2040
 
{
2041
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2042
 
 
2043
 
  return self->priv->attention_icon_name;
2044
 
}
2045
 
 
2046
 
/**
2047
 
        app_indicator_get_menu:
2048
 
        @self: The #AppIndicator object to use
2049
 
 
2050
 
        Gets the menu being used for this application indicator.
2051
 
        Wrapper function for property #AppIndicator:menu.
2052
 
 
2053
 
        Return value: A #GtkMenu object or %NULL if one hasn't been set.
2054
 
*/
2055
 
GtkMenu *
2056
 
app_indicator_get_menu (AppIndicator *self)
2057
 
{
2058
 
        AppIndicatorPrivate *priv;
2059
 
 
2060
 
        g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2061
 
 
2062
 
        priv = self->priv;
2063
 
 
2064
 
        return GTK_MENU(priv->menu);
2065
 
}
2066
 
 
2067
 
/**
2068
 
        app_indicator_get_label:
2069
 
        @self: The #AppIndicator object to use
2070
 
 
2071
 
        Wrapper function for property #AppIndicator:label.
2072
 
 
2073
 
        Return value: The current label.
2074
 
*/
2075
 
const gchar *
2076
 
app_indicator_get_label (AppIndicator *self)
2077
 
{
2078
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2079
 
 
2080
 
  return self->priv->label;
2081
 
}
2082
 
 
2083
 
/**
2084
 
        app_indicator_get_label_guide:
2085
 
        @self: The #AppIndicator object to use
2086
 
 
2087
 
        Wrapper function for property #AppIndicator:label-guide.
2088
 
 
2089
 
        Return value: The current label guide.
2090
 
*/
2091
 
const gchar *
2092
 
app_indicator_get_label_guide (AppIndicator *self)
2093
 
{
2094
 
  g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
2095
 
 
2096
 
  return self->priv->label_guide;
2097
 
}
2098
 
 
2099
 
/**
2100
 
        app_indicator_get_ordering_index:
2101
 
        @self: The #AppIndicator object to use
2102
 
 
2103
 
        Wrapper function for property #AppIndicator:ordering-index.
2104
 
 
2105
 
        Return value: The current ordering index.
2106
 
*/
2107
 
guint32
2108
 
app_indicator_get_ordering_index (AppIndicator *self)
2109
 
{
2110
 
        g_return_val_if_fail (IS_APP_INDICATOR (self), 0);
2111
 
 
2112
 
        if (self->priv->ordering_index == 0) {
2113
 
                return generate_id(self->priv->category, self->priv->id);
2114
 
        } else {
2115
 
                return self->priv->ordering_index;
2116
 
        }
2117
 
}
2118