~ubuntu-branches/ubuntu/trusty/indicator-sync/trusty

« back to all changes in this revision

Viewing changes to src/indicator/indicator-sync.c

  • Committer: Package Import Robot
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2012-08-28 16:25:25 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120828162525-v3mx9o332fqrseu8
Tags: 12.10.1-0ubuntu1
* New upstream release.
* debian/control:
  - rename gir1.2-syncclient-12.10 to gir1.2-syncmenu-0.1.
  - rename libindicator-sync-client1 to libsync-menu1.
  - rename libindicator-sync-client-dev to libsync-menu-dev.
  - add gir1.2-dbusmenu-glib-0.4 to Build-Depends.
  - add dbus-test-runner to Build-Depends.
* debian/*.install:
  - rename files as per package name changes.
  - update libsync-menu-dev.install to the library name.
  - update libsync-menu1.install to the library name.
  - update libsync-menu1.symbols to the library name.
  - rename /usr/include/indicator-sync-0.1 to /usr/include/sync-menu-0.1.
* debian/patches/upstream-soname-versioning.patch,
  debian/patches/upstream-pkgconfig-fixes.patch: dropped, no longer needed.
* debian/libsync-menu1.symbols:
  - update symbols that were renamed; keep the old ones as missing.
* debian/patches/upstream-watchers-client-test.patch: fix up the client test
  to avoid failing due to watchers not being allowed to connect.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   An indicator to show SyncMenuApps' states and menus
 
3
 
 
4
   Copyright 2012 Canonical Ltd.
 
5
 
 
6
   Authors:
 
7
     Charles Kerr <charles.kerr@canonical.com>
 
8
 
 
9
   This program is free software: you can redistribute it and/or modify it 
 
10
   under the terms of the GNU General Public License version 3,
 
11
   as published by the Free Software Foundation.
 
12
 
 
13
   This program is distributed in the hope that it will be useful,
 
14
   but WITHOUT ANY WARRANTY; without even the implied warranties of
 
15
   MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
 
16
   See the GNU General Public License for more details.
 
17
 
 
18
   You should have received a copy of the GNU General Public License along 
 
19
   with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
 #include "config.h"
 
24
#endif
 
25
 
 
26
#include <glib.h>
 
27
#include <glib-object.h>
 
28
#include <glib/gi18n-lib.h>
 
29
#include <gtk/gtk.h>
 
30
 
 
31
#include <libdbusmenu-gtk/menu.h>
 
32
#include <libdbusmenu-gtk/menuitem.h>
 
33
 
 
34
#include <libindicator/indicator.h>
 
35
#include <libindicator/indicator-object.h>
 
36
#include <libindicator/indicator-service-manager.h>
 
37
 
 
38
#include <libido/idoswitchmenuitem.h>
 
39
 
 
40
#include "dbus-shared.h"
 
41
#include "sync-menu/sync-app.h"
 
42
#include "sync-menu/sync-enum.h"
 
43
#include "sync-service-dbus.h"
 
44
 
 
45
#define INDICATOR_SYNC_TYPE            (indicator_sync_get_type ())
 
46
#define INDICATOR_SYNC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_SYNC_TYPE, IndicatorSync))
 
47
#define INDICATOR_SYNC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_SYNC_TYPE, IndicatorSyncClass))
 
48
#define IS_INDICATOR_SYNC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_SYNC_TYPE))
 
49
#define IS_INDICATOR_SYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_SYNC_TYPE))
 
50
#define INDICATOR_SYNC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_SYNC_TYPE, IndicatorSyncClass))
 
51
 
 
52
typedef struct _IndicatorSync      IndicatorSync;
 
53
typedef struct _IndicatorSyncClass IndicatorSyncClass;
 
54
 
 
55
struct _IndicatorSyncClass
 
56
{
 
57
  IndicatorObjectClass parent_class;
 
58
};
 
59
 
 
60
struct _IndicatorSync
 
61
{
 
62
  IndicatorObject            parent;
 
63
  IndicatorObjectEntry       entry;
 
64
  IndicatorServiceManager  * service_manager;
 
65
  DbusSyncService          * sync_service_proxy;
 
66
  DbusmenuGtkClient        * menu_client;
 
67
};
 
68
 
 
69
GType indicator_sync_get_type (void);
 
70
 
 
71
/* Indicator Module Config */
 
72
INDICATOR_SET_VERSION
 
73
INDICATOR_SET_TYPE(INDICATOR_SYNC_TYPE)
 
74
 
 
75
static void indicator_sync_class_init (IndicatorSyncClass *klass);
 
76
static void indicator_sync_init       (IndicatorSync *self);
 
77
static void indicator_sync_dispose    (GObject *object);
 
78
static void indicator_sync_finalize   (GObject *object);
 
79
 
 
80
static gboolean new_item_app (DbusmenuMenuitem * newitem,
 
81
                              DbusmenuMenuitem * parent,
 
82
                              DbusmenuClient   * client,
 
83
                              gpointer           user_data);
 
84
 
 
85
static gboolean new_item_prog (DbusmenuMenuitem * newitem,
 
86
                               DbusmenuMenuitem * parent,
 
87
                               DbusmenuClient   * client,
 
88
                               gpointer           user_data);
 
89
 
 
90
static const gchar * calculate_icon_name (IndicatorSync * self);
 
91
 
 
92
static void on_service_manager_connection_changed (IndicatorServiceManager * sm,
 
93
                                                   gboolean connected,
 
94
                                                   gpointer user_data);
 
95
 
 
96
/***
 
97
****
 
98
***/
 
99
 
 
100
static GList*
 
101
get_entries (IndicatorObject * io)
 
102
{
 
103
  return g_list_prepend (NULL, &INDICATOR_SYNC(io)->entry);
 
104
}
 
105
 
 
106
G_DEFINE_TYPE (IndicatorSync, indicator_sync, INDICATOR_OBJECT_TYPE);
 
107
 
 
108
static void
 
109
indicator_sync_class_init (IndicatorSyncClass *klass)
 
110
{
 
111
  GObjectClass * object_class = G_OBJECT_CLASS (klass);
 
112
  object_class->dispose = indicator_sync_dispose;
 
113
  object_class->finalize = indicator_sync_finalize;
 
114
 
 
115
  IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
 
116
  io_class->get_entries = get_entries;
 
117
}
 
118
  
 
119
static void
 
120
indicator_sync_init (IndicatorSync *self)
 
121
{
 
122
  g_object_set (self, INDICATOR_OBJECT_DEFAULT_VISIBILITY, FALSE, NULL);
 
123
 
 
124
  /* init the menu */
 
125
  DbusmenuGtkMenu * menu = dbusmenu_gtkmenu_new (SYNC_SERVICE_DBUS_NAME,
 
126
                                                 SYNC_SERVICE_DBUS_MENU_OBJECT);
 
127
  self->menu_client = dbusmenu_gtkmenu_get_client (menu);
 
128
  dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(self->menu_client),
 
129
                                    APPLICATION_MENUITEM_TYPE,
 
130
                                    new_item_app);
 
131
  dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(self->menu_client),
 
132
                                    SYNC_MENU_PROGRESS_MENUITEM_TYPE,
 
133
                                    new_item_prog);
 
134
 
 
135
  /* init the entry */
 
136
  self->entry.label = NULL; /* no label */
 
137
  self->entry.image = g_object_ref_sink (gtk_image_new_from_icon_name (calculate_icon_name (self), GTK_ICON_SIZE_BUTTON));
 
138
  self->entry.menu = g_object_ref_sink (menu);
 
139
  self->entry.name_hint = PACKAGE;
 
140
  self->entry.accessible_desc = NULL;
 
141
  gtk_widget_show (GTK_WIDGET(self->entry.image));
 
142
 
 
143
  /* init the service manager */
 
144
  self->service_manager = indicator_service_manager_new_version (
 
145
                            SYNC_SERVICE_DBUS_NAME, 1);
 
146
  g_signal_connect (self->service_manager,
 
147
                    INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE,
 
148
                    G_CALLBACK(on_service_manager_connection_changed), self);
 
149
}
 
150
 
 
151
static void
 
152
indicator_sync_dispose (GObject *object)
 
153
{
 
154
  IndicatorSync * self = INDICATOR_SYNC(object);
 
155
  g_return_if_fail (self != NULL);
 
156
 
 
157
  g_clear_object (&self->sync_service_proxy);
 
158
  g_clear_object (&self->service_manager);
 
159
  g_clear_object (&self->entry.image);
 
160
 
 
161
  if (self->entry.menu)
 
162
    {
 
163
      self->menu_client = NULL;
 
164
      g_clear_object (&self->entry.menu);
 
165
    }
 
166
 
 
167
  G_OBJECT_CLASS (indicator_sync_parent_class)->dispose (object);
 
168
}
 
169
 
 
170
static void
 
171
indicator_sync_finalize (GObject *object)
 
172
{
 
173
  G_OBJECT_CLASS (indicator_sync_parent_class)->finalize (object);
 
174
}
 
175
 
 
176
/****
 
177
*****
 
178
****/
 
179
 
 
180
static SyncMenuState
 
181
get_service_state (IndicatorSync * self)
 
182
{
 
183
  if (self->sync_service_proxy == NULL)
 
184
    {
 
185
      return SYNC_MENU_STATE_IDLE;
 
186
    }
 
187
 
 
188
  return dbus_sync_service_get_state (self->sync_service_proxy);
 
189
}
 
190
 
 
191
static gboolean
 
192
get_service_paused (IndicatorSync * self)
 
193
{
 
194
  return (self->sync_service_proxy != NULL)
 
195
      && (dbus_sync_service_get_paused (self->sync_service_proxy));
 
196
}
 
197
 
 
198
static gboolean
 
199
get_service_count (IndicatorSync * self)
 
200
{
 
201
  int count = 0;
 
202
 
 
203
  if (self->sync_service_proxy != NULL)
 
204
    {
 
205
      count = dbus_sync_service_get_client_count (self->sync_service_proxy);
 
206
    }
 
207
 
 
208
  return count;
 
209
}
 
210
 
 
211
/* show the indicator only if the service has SyncMenuApps */
 
212
static void
 
213
update_visibility (IndicatorSync * self)
 
214
{
 
215
  const gboolean new_visible = get_service_count (self) > 0;
 
216
  g_debug (G_STRLOC" setting visibility flag to %d", (int)new_visible);
 
217
  indicator_object_set_visible (INDICATOR_OBJECT(self), new_visible);
 
218
}
 
219
 
 
220
/* update our a11y text based on the sync service's state */
 
221
static void
 
222
update_accessible_title (IndicatorSync * self)
 
223
{
 
224
  const SyncMenuState state = get_service_state (self);
 
225
  const gboolean paused = get_service_paused (self);
 
226
 
 
227
  const gchar * desc = NULL;
 
228
 
 
229
  if (state == SYNC_MENU_STATE_ERROR)
 
230
    {
 
231
      desc = _("Sync (error)");
 
232
    }
 
233
  else if (state == SYNC_MENU_STATE_SYNCING)
 
234
    {
 
235
      desc = _("Sync (syncing)");
 
236
    }
 
237
  else if (paused)
 
238
    {
 
239
      desc = _("Sync (paused)");
 
240
    }
 
241
  else
 
242
    {
 
243
      desc = _("Sync");
 
244
    }
 
245
 
 
246
  if (g_strcmp0 (self->entry.accessible_desc, desc))
 
247
    {
 
248
      g_debug (G_STRLOC" setting accessible_desc to '%s'", desc);
 
249
      self->entry.accessible_desc = desc;
 
250
      g_signal_emit (self,
 
251
                     INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID,
 
252
                     0,
 
253
                     &self->entry);
 
254
    }
 
255
}
 
256
 
 
257
/* figure out what icon to use */
 
258
static const gchar *
 
259
calculate_icon_name (IndicatorSync * self)
 
260
{
 
261
  const gchar * icon_name;
 
262
  const SyncMenuState state = get_service_state (self);
 
263
  const gboolean paused = get_service_paused (self);
 
264
 
 
265
  if (state == SYNC_MENU_STATE_ERROR)
 
266
    {
 
267
      icon_name = "sync-error";
 
268
    }
 
269
  else if (state == SYNC_MENU_STATE_SYNCING)
 
270
    {
 
271
      icon_name = "sync-syncing";
 
272
    }
 
273
  else if (paused)
 
274
    {
 
275
      icon_name = "sync-paused";
 
276
    }
 
277
  else
 
278
    {
 
279
      icon_name = "sync-idle";
 
280
    }
 
281
 
 
282
  return icon_name;
 
283
}
 
284
 
 
285
/* update our icon based on the service's state */
 
286
static void
 
287
update_icon (IndicatorSync * self)
 
288
{
 
289
  g_return_if_fail (IS_INDICATOR_SYNC(self));
 
290
 
 
291
  const gchar * const icon_name = calculate_icon_name (self);
 
292
  g_debug (G_STRLOC" setting icon to '%s'", icon_name);
 
293
  gtk_image_set_from_icon_name (GTK_IMAGE(self->entry.image), icon_name, GTK_ICON_SIZE_MENU);
 
294
}
 
295
 
 
296
static void
 
297
on_service_client_count_changed (GObject     * o           G_GNUC_UNUSED,
 
298
                                 GParamSpec  * pspec       G_GNUC_UNUSED,
 
299
                                 gpointer      user_data)
 
300
{
 
301
  IndicatorSync * self = INDICATOR_SYNC(user_data);
 
302
  g_return_if_fail (self != NULL);
 
303
 
 
304
  update_visibility (self);
 
305
}
 
306
 
 
307
static void
 
308
on_service_state_changed (GObject     * o          G_GNUC_UNUSED,
 
309
                          GParamSpec  * pspec      G_GNUC_UNUSED,
 
310
                          gpointer      user_data)
 
311
{
 
312
  IndicatorSync * self = INDICATOR_SYNC(user_data);
 
313
  g_return_if_fail (self != NULL);
 
314
 
 
315
  update_icon (self);
 
316
  update_accessible_title (self);
 
317
}
 
318
 
 
319
static void
 
320
on_service_paused_changed (GObject     * o          G_GNUC_UNUSED,
 
321
                           GParamSpec  * pspec      G_GNUC_UNUSED,
 
322
                           gpointer      user_data)
 
323
{
 
324
  IndicatorSync * self = INDICATOR_SYNC(user_data);
 
325
  g_return_if_fail (self != NULL);
 
326
 
 
327
  update_icon (self);
 
328
  update_accessible_title (self);
 
329
}
 
330
 
 
331
 
 
332
/* manage our service proxy based on whether or not the manager's connected */
 
333
static void 
 
334
on_service_manager_connection_changed (IndicatorServiceManager  * sm,
 
335
                                       gboolean                   connected,
 
336
                                       gpointer                   user_data)
 
337
{
 
338
  IndicatorSync * self = INDICATOR_SYNC(user_data);
 
339
  g_return_if_fail (self != NULL);
 
340
 
 
341
  if (!connected)
 
342
    {
 
343
      g_clear_object (&self->sync_service_proxy);
 
344
      update_visibility (self);
 
345
    }
 
346
  else if (self->sync_service_proxy == NULL)
 
347
    {
 
348
      GError * err = NULL;
 
349
      self->sync_service_proxy = dbus_sync_service_proxy_new_for_bus_sync (
 
350
                                  G_BUS_TYPE_SESSION,
 
351
                                  G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
 
352
                                  SYNC_SERVICE_DBUS_NAME,
 
353
                                  SYNC_SERVICE_DBUS_OBJECT,
 
354
                                  NULL,
 
355
                                  &err);
 
356
      if (err != NULL)
 
357
        {
 
358
          g_warning ("Indicator couldn't create SyncService proxy: %s", err->message);
 
359
          g_clear_error (&err);
 
360
        }
 
361
      else
 
362
        {
 
363
          GObject * o = G_OBJECT (self->sync_service_proxy);
 
364
          g_signal_connect (o, "notify::state",
 
365
                            G_CALLBACK(on_service_state_changed), self);
 
366
          g_signal_connect (o, "notify::paused",
 
367
                            G_CALLBACK(on_service_paused_changed), self);
 
368
          g_signal_connect (o, "notify::client-count",
 
369
                            G_CALLBACK(on_service_client_count_changed), self);
 
370
          on_service_state_changed (o, NULL, self);
 
371
          on_service_paused_changed (o, NULL, self);
 
372
          on_service_client_count_changed (o, NULL, self);
 
373
        }
 
374
    }
 
375
}
 
376
 
 
377
/***
 
378
****
 
379
****  APPLICATION_MENUITEM_TYPE handler
 
380
****
 
381
***/
 
382
 
 
383
#define PROP_TYPE                DBUSMENU_MENUITEM_PROP_TYPE
 
384
#define PROP_TOGGLE_STATE        DBUSMENU_MENUITEM_PROP_TOGGLE_STATE
 
385
#define TOGGLE_STATE_CHECKED     DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED
 
386
#define SIGNAL_PROPERTY_CHANGED  DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED
 
387
 
 
388
static int aligned_left_margin = 0;
 
389
 
 
390
/*
 
391
 * This ugly little hack aligns the SyncMenuApp's exported GtkMenuItems'
 
392
 * text with the AppMenuItem's GtkMenuItem text.
 
393
 *
 
394
 * Ideally we'd do this by setting the widgets' left margins when
 
395
 * they are created, but that happens deep inside dbusmenu-gtk.
 
396
 * So we need a way to get the widgets after they're created but
 
397
 * before the users see them.
 
398
 *
 
399
 * The approach used here is to listen for the "map" signal from the
 
400
 * widgets we /do/ create (in new_item_app()) so that we know when
 
401
 * the menu is being popped up. When mapping happens, we walk through
 
402
 * the menuitems and align them.
 
403
 */
 
404
static void
 
405
on_app_widget_shown (GtkWidget * unused, DbusmenuClient * client)
 
406
{
 
407
  /* init the tweak key */
 
408
  static GQuark tweak_quark = 0;
 
409
  if (G_UNLIKELY(tweak_quark == 0))
 
410
    {
 
411
      tweak_quark = g_quark_from_static_string ("margin tweaked by i-sync");
 
412
    }
 
413
 
 
414
  DbusmenuMenuitem * root = dbusmenu_client_get_root (client);
 
415
  if (root == NULL)
 
416
    {
 
417
      return;
 
418
    }
 
419
 
 
420
  GList * l;
 
421
  GList * children = dbusmenu_menuitem_get_children (root);
 
422
  DbusmenuGtkClient * gtk_client = DBUSMENU_GTKCLIENT (client);
 
423
  for (l=children; l!=NULL; l=l->next)
 
424
    {
 
425
      DbusmenuMenuitem * child = DBUSMENU_MENUITEM(l->data);
 
426
    
 
427
      /* AppMenuItems are already indented by their icons */
 
428
      const gchar * type = dbusmenu_menuitem_property_get (child, PROP_TYPE);
 
429
      if (!g_strcmp0 (type, APPLICATION_MENUITEM_TYPE))
 
430
        {
 
431
          continue;
 
432
        }
 
433
 
 
434
      GtkMenuItem * gmi = dbusmenu_gtkclient_menuitem_get (gtk_client, child);
 
435
       
 
436
      GObject * o = G_OBJECT(gmi);
 
437
      if (g_object_get_qdata (o, tweak_quark) == NULL)
 
438
        {
 
439
          g_object_set_qdata (o, tweak_quark, GINT_TO_POINTER(TRUE));
 
440
          gtk_widget_set_margin_left (GTK_WIDGET(o), aligned_left_margin);
 
441
        }
 
442
    }
 
443
}
 
444
 
 
445
struct AppWidgets
 
446
{
 
447
  GtkWidget * label;
 
448
  GtkWidget * icon;
 
449
  GtkWidget * gmi;
 
450
};
 
451
 
 
452
static void
 
453
app_update_check (DbusmenuMenuitem * mi, struct AppWidgets * widgets)
 
454
{
 
455
  const gint checked = dbusmenu_menuitem_property_get_int (mi, PROP_TOGGLE_STATE);
 
456
 
 
457
  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widgets->gmi),
 
458
                                  checked == TOGGLE_STATE_CHECKED);
 
459
}
 
460
 
 
461
static void
 
462
app_update_icon (DbusmenuMenuitem * mi, struct AppWidgets * w)
 
463
{
 
464
  const gchar * icon_name = dbusmenu_menuitem_property_get (mi, APPLICATION_MENUITEM_PROP_ICON);
 
465
 
 
466
  gtk_image_set_from_icon_name (GTK_IMAGE(w->icon), icon_name, GTK_ICON_SIZE_MENU);
 
467
}
 
468
 
 
469
static void
 
470
app_update_label (DbusmenuMenuitem * mi, struct AppWidgets * w)
 
471
{
 
472
  const gchar * name = dbusmenu_menuitem_property_get (mi, APPLICATION_MENUITEM_PROP_NAME);
 
473
  const SyncMenuState state = dbusmenu_menuitem_property_get_int (mi, APPLICATION_MENUITEM_PROP_STATE);
 
474
 
 
475
  if (state != SYNC_MENU_STATE_ERROR)
 
476
    {
 
477
      gtk_label_set_text (GTK_LABEL(w->label), name);
 
478
    }
 
479
  else
 
480
    {
 
481
      /* FIXME: it would be nice if the error color played nicely with themes */
 
482
      gchar * escaped = g_markup_escape_text (name, -1);
 
483
      gchar * markup = g_strdup_printf ("<span foreground=\"red\">%s</span>", escaped);
 
484
      gtk_label_set_markup (GTK_LABEL(w->label), markup);
 
485
      g_free (markup);
 
486
      g_free (escaped);
 
487
    }
 
488
}
 
489
 
 
490
static void
 
491
on_app_property_changed (DbusmenuMenuitem  * mi,
 
492
                         gchar             * prop,
 
493
                         GVariant          * value,
 
494
                         struct AppWidgets * w)
 
495
{
 
496
  if (!g_strcmp0 (prop, APPLICATION_MENUITEM_PROP_STATE) ||
 
497
      !g_strcmp0 (prop, APPLICATION_MENUITEM_PROP_NAME))
 
498
    {
 
499
      app_update_label (mi, w);
 
500
    }
 
501
  else if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_ICON))
 
502
    {
 
503
      app_update_icon (mi, w);
 
504
    }
 
505
  else if (!g_strcmp0(prop, PROP_TOGGLE_STATE))
 
506
    {
 
507
      app_update_check (mi, w);
 
508
    }
 
509
}
 
510
 
 
511
/* build a gtkmenuitem to represent an AppMenuItem */
 
512
static gboolean
 
513
new_item_app (DbusmenuMenuitem  * newitem,
 
514
              DbusmenuMenuitem  * parent,
 
515
              DbusmenuClient    * client,
 
516
              gpointer            user_data)
 
517
{
 
518
  struct AppWidgets * w;
 
519
 
 
520
  /* build the display widgets and initialize them */
 
521
  w = g_new0 (struct AppWidgets, 1);
 
522
  w->gmi = ido_switch_menu_item_new ();
 
523
  w->icon = gtk_image_new ();
 
524
  w->label = gtk_label_new (NULL);
 
525
  app_update_icon  (newitem, w);
 
526
  app_update_label (newitem, w);
 
527
  app_update_check (newitem, w);
 
528
 
 
529
  /* layout & styling */
 
530
  g_signal_connect (w->gmi, "map", G_CALLBACK(on_app_widget_shown), client);
 
531
  gint padding = 4;
 
532
  gtk_widget_style_get(GTK_WIDGET(w->gmi), "toggle-spacing", &padding, NULL);
 
533
  GtkWidget * hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding);
 
534
  int icon_width = 0;
 
535
  gtk_widget_get_preferred_width (w->icon, NULL, &icon_width);
 
536
  aligned_left_margin = padding + icon_width;
 
537
  gtk_misc_set_alignment(GTK_MISC(w->icon), 1.0 /* right aligned */, 0.5);
 
538
  gtk_box_pack_start (GTK_BOX (hbox), w->icon, FALSE, FALSE, 0);
 
539
  gtk_misc_set_alignment (GTK_MISC(w->label), 0.0, 0.5);
 
540
  gtk_box_pack_start (GTK_BOX(hbox), w->label, TRUE, TRUE, 0);
 
541
  gtk_container_add (ido_switch_menu_item_get_content_area(IDO_SWITCH_MENU_ITEM(w->gmi)), hbox);
 
542
  gtk_widget_show_all (w->gmi);
 
543
 
 
544
  /* let dbusmenu attach its standard handlers */
 
545
  dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client),
 
546
                                   newitem,
 
547
                                   GTK_MENU_ITEM(w->gmi),
 
548
                                   parent);
 
549
 
 
550
  /* listen for property changes that would affect how we draw this item */
 
551
  g_signal_connect_data (newitem, SIGNAL_PROPERTY_CHANGED,
 
552
                         G_CALLBACK(on_app_property_changed),
 
553
                         w, (GClosureNotify)g_free, 0);
 
554
 
 
555
  return TRUE; /* success */
 
556
}
 
557
 
 
558
 
 
559
/***
 
560
****  SYNC_MENU_PROGRESS_MENUITEM_TYPE handler
 
561
****
 
562
****  FIXME: this would be a good candiate for promotion into IDO
 
563
****  if any other indicators need this in the future.
 
564
***/
 
565
 
 
566
 
 
567
struct ProgWidgets
 
568
{
 
569
  GtkWidget * gmi;
 
570
  GtkWidget * label;
 
571
  GtkWidget * progress_bar;
 
572
};
 
573
 
 
574
#define PROP_PERCENT  SYNC_MENU_PROGRESS_MENUITEM_PROP_PERCENT_DONE
 
575
#define PROP_LABEL    DBUSMENU_MENUITEM_PROP_LABEL
 
576
 
 
577
static inline int
 
578
get_percent_done (DbusmenuMenuitem * mi)
 
579
{
 
580
  return CLAMP (dbusmenu_menuitem_property_get_int (mi, PROP_PERCENT), 0, 100);
 
581
}
 
582
 
 
583
static void
 
584
prog_update_name (DbusmenuMenuitem * mi, struct ProgWidgets * w)
 
585
{
 
586
  const int percent_done = get_percent_done (mi);
 
587
  const gchar * name = dbusmenu_menuitem_property_get (mi, PROP_LABEL);
 
588
 
 
589
  char * text = g_strdup_printf ("%s ... %d%%", name, percent_done);
 
590
  gtk_label_set_text (GTK_LABEL(w->label), text);
 
591
  g_free (text);
 
592
}
 
593
 
 
594
static void
 
595
prog_update_percent (DbusmenuMenuitem * mi, struct ProgWidgets * w)
 
596
{
 
597
  const int percent_done = get_percent_done (mi);
 
598
 
 
599
  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(w->progress_bar),
 
600
                                 percent_done/100.0);
 
601
}
 
602
 
 
603
/* update our display when some of the menuitem's properties change */
 
604
static void
 
605
on_prog_property_changed (DbusmenuMenuitem    * mi,
 
606
                          gchar               * prop,
 
607
                          GVariant            * value,
 
608
                          struct ProgWidgets  * w)
 
609
{
 
610
  if (!g_strcmp0 (prop, PROP_PERCENT))
 
611
    {
 
612
      prog_update_name (mi, w);
 
613
      prog_update_percent (mi, w);
 
614
    }
 
615
  else if (!g_strcmp0 (prop, PROP_LABEL))
 
616
    {
 
617
      prog_update_name (mi, w);
 
618
    }
 
619
}
 
620
 
 
621
/* Calculate a uniform width for the progmenus' progress bars & labels.
 
622
   This is kind of a fuzzy guesswork of wanting to ellipsize filenames
 
623
   after they get about 40 characters or so wide. */
 
624
static gint
 
625
calculate_prog_menuitem_preferred_width (GtkWidget * w)
 
626
{
 
627
   int width;
 
628
   const gchar * const test_case = "THIS STRING HAS FORTY CHARACTERS IN IT..";
 
629
 
 
630
   PangoLayout * pl;
 
631
   pl = gtk_widget_create_pango_layout (w, test_case);
 
632
   pango_layout_get_pixel_size (pl, &width, NULL);
 
633
   g_clear_object (&pl);
 
634
 
 
635
   return width;
 
636
}
 
637
 
 
638
static gboolean
 
639
new_item_prog (DbusmenuMenuitem  * newitem,
 
640
               DbusmenuMenuitem  * parent,
 
641
               DbusmenuClient    * client,
 
642
               gpointer            user_data)
 
643
{
 
644
  struct ProgWidgets * w;
 
645
 
 
646
  /* build the display widgets and initialize them */
 
647
  w = g_new0 (struct ProgWidgets, 1);
 
648
  w->label = gtk_label_new (NULL);
 
649
  w->progress_bar = gtk_progress_bar_new ();
 
650
  w->gmi = gtk_menu_item_new ();
 
651
  prog_update_name (newitem, w);
 
652
  prog_update_percent (newitem, w);
 
653
 
 
654
  /* layout & styling */
 
655
  GtkWidget * grid = gtk_grid_new ();
 
656
  const int width = calculate_prog_menuitem_preferred_width (w->label);
 
657
  gtk_widget_set_size_request (w->label, width, -1);
 
658
  gtk_label_set_ellipsize (GTK_LABEL(w->label), PANGO_ELLIPSIZE_MIDDLE);
 
659
  gtk_misc_set_alignment(GTK_MISC(w->label), 0.0, 0.5);
 
660
  gtk_grid_attach (GTK_GRID(grid), w->label, 0, 0, 1, 1);
 
661
  gtk_widget_set_size_request (w->progress_bar, width, -1);
 
662
  gtk_grid_attach (GTK_GRID(grid), w->progress_bar, 0, 1, 1, 1);
 
663
  gtk_container_add (GTK_CONTAINER(w->gmi), grid);
 
664
  gtk_container_set_border_width (GTK_CONTAINER(w->gmi), 4);
 
665
  gtk_widget_show_all (w->gmi);
 
666
 
 
667
  /* let dbusmenu attach its standard handlers */
 
668
  dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client),
 
669
                                   newitem,
 
670
                                   GTK_MENU_ITEM(w->gmi),
 
671
                                   parent);
 
672
 
 
673
  /* listen for property changes that would affect how we draw this item */
 
674
  g_signal_connect_data (newitem, SIGNAL_PROPERTY_CHANGED,
 
675
                         G_CALLBACK(on_prog_property_changed),
 
676
                         w, (GClosureNotify)g_free, 0);
 
677
 
 
678
 
 
679
  return TRUE; /* success */
 
680
}