~ps-jenkins/indicator-power/latestsnapshot-12.10.6+13.10.20131008-0ubuntu1

« back to all changes in this revision

Viewing changes to src/service.c

  • Committer: Tarmac
  • Author(s): Charles Kerr
  • Date: 2013-06-19 21:13:09 UTC
  • mfrom: (167.1.32 gmenuify)
  • Revision ID: tarmac-20130619211309-nj444ogmrhsi7r9f
Convert the power indicator into a service that exports GMenus and GActions in accordance with our indicator-ng design.

Approved by PS Jenkins bot, Ted Gould.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2013 Canonical Ltd.
 
3
 *
 
4
 * Authors:
 
5
 *   Charles Kerr <charles.kerr@canonical.com>
 
6
 *   Ted Gould <ted@canonical.com>
 
7
 *
 
8
 * This program is free software: you can redistribute it and/or modify it
 
9
 * under the terms of the GNU General Public License version 3, as published
 
10
 * by the Free Software Foundation.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful, but
 
13
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 
14
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
15
 * PURPOSE.  See the GNU General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License along
 
18
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
 */
 
20
 
 
21
#include "config.h"
 
22
 
 
23
#include <glib/gi18n.h>
 
24
#include <gio/gio.h>
 
25
 
 
26
#include "device.h"
 
27
#include "device-provider.h"
 
28
#include "service.h"
 
29
 
 
30
#define BUS_NAME "com.canonical.indicator.power"
 
31
#define BUS_PATH "/com/canonical/indicator/power"
 
32
 
 
33
#define SETTINGS_SHOW_TIME_S "show-time"
 
34
#define SETTINGS_ICON_POLICY_S "icon-policy"
 
35
 
 
36
G_DEFINE_TYPE (IndicatorPowerService,
 
37
               indicator_power_service,
 
38
               G_TYPE_OBJECT)
 
39
 
 
40
enum
 
41
{
 
42
  SIGNAL_NAME_LOST,
 
43
  LAST_SIGNAL
 
44
};
 
45
 
 
46
static guint signals[LAST_SIGNAL] = { 0 };
 
47
 
 
48
enum
 
49
{
 
50
  PROP_0,
 
51
  PROP_DEVICE_PROVIDER,
 
52
  LAST_PROP
 
53
};
 
54
 
 
55
static GParamSpec * properties[LAST_PROP];
 
56
 
 
57
enum
 
58
{
 
59
  SECTION_HEADER    = (1<<0),
 
60
  SECTION_DEVICES   = (1<<1),
 
61
  SECTION_SETTINGS  = (1<<2),
 
62
};
 
63
 
 
64
enum
 
65
{
 
66
  PROFILE_DESKTOP,
 
67
  PROFILE_GREETER,
 
68
  N_PROFILES
 
69
};
 
70
 
 
71
static const char * const menu_names[N_PROFILES] =
 
72
{
 
73
  "desktop",
 
74
  "greeter"
 
75
};
 
76
 
 
77
enum
 
78
{
 
79
  POWER_INDICATOR_ICON_POLICY_PRESENT,
 
80
  POWER_INDICATOR_ICON_POLICY_CHARGE,
 
81
  POWER_INDICATOR_ICON_POLICY_NEVER
 
82
};
 
83
 
 
84
struct ProfileMenuInfo
 
85
{
 
86
  /* the root level -- the header is the only child of this */
 
87
  GMenu * menu;
 
88
 
 
89
  /* parent of the sections. This is the header's submenu */
 
90
  GMenu * submenu;
 
91
 
 
92
  guint export_id;
 
93
};
 
94
 
 
95
struct _IndicatorPowerServicePrivate
 
96
{
 
97
  GCancellable * cancellable;
 
98
 
 
99
  GSettings * settings;
 
100
 
 
101
  guint own_id;
 
102
  guint actions_export_id;
 
103
  GDBusConnection * conn;
 
104
 
 
105
  struct ProfileMenuInfo menus[N_PROFILES];
 
106
 
 
107
  GSimpleActionGroup * actions;
 
108
  GSimpleAction * header_action;
 
109
  GSimpleAction * show_time_action;
 
110
 
 
111
  IndicatorPowerDevice * primary_device;
 
112
  GList * devices; /* IndicatorPowerDevice */
 
113
 
 
114
  IndicatorPowerDeviceProvider * device_provider;
 
115
};
 
116
 
 
117
typedef IndicatorPowerServicePrivate priv_t;
 
118
 
 
119
/***
 
120
****
 
121
****  DEVICES
 
122
****
 
123
***/
 
124
 
 
125
/* the higher the weight, the more interesting the device */
 
126
static int
 
127
get_device_kind_weight (const IndicatorPowerDevice * device)
 
128
{
 
129
  UpDeviceKind kind;
 
130
  static gboolean initialized = FALSE;
 
131
  static int weights[UP_DEVICE_KIND_LAST];
 
132
 
 
133
  kind = indicator_power_device_get_kind (device);
 
134
  g_return_val_if_fail (0<=kind && kind<UP_DEVICE_KIND_LAST, 0);
 
135
 
 
136
  if (G_UNLIKELY(!initialized))
 
137
    {
 
138
      int i;
 
139
 
 
140
      initialized = TRUE;
 
141
 
 
142
      for (i=0; i<UP_DEVICE_KIND_LAST; i++)
 
143
        weights[i] = 1;
 
144
      weights[UP_DEVICE_KIND_BATTERY] = 2;
 
145
      weights[UP_DEVICE_KIND_LINE_POWER] = 0;
 
146
    }
 
147
 
 
148
  return weights[kind];
 
149
}
 
150
 
 
151
/* sort devices from most interesting to least interesting on this criteria:
 
152
   1. discharging items from least time remaining until most time remaining
 
153
   2. discharging items with an unknown time remaining
 
154
   3. charging items from most time left to charge to least time left to charge
 
155
   4. charging items with an unknown time remaining
 
156
   5. batteries, then non-line power, then line-power */
 
157
static gint
 
158
device_compare_func (gconstpointer ga, gconstpointer gb)
 
159
{
 
160
  int ret;
 
161
  int state;
 
162
  const IndicatorPowerDevice * a = ga;
 
163
  const IndicatorPowerDevice * b = gb;
 
164
  const int a_state = indicator_power_device_get_state (a);
 
165
  const int b_state = indicator_power_device_get_state (b);
 
166
  const gdouble a_percentage = indicator_power_device_get_percentage (a);
 
167
  const gdouble b_percentage = indicator_power_device_get_percentage (b);
 
168
  const time_t a_time = indicator_power_device_get_time (a);
 
169
  const time_t b_time = indicator_power_device_get_time (b);
 
170
 
 
171
  ret = 0;
 
172
 
 
173
  state = UP_DEVICE_STATE_DISCHARGING;
 
174
  if (!ret && ((a_state == state) || (b_state == state)))
 
175
    {
 
176
      if (a_state != state) /* b is discharging */
 
177
        {
 
178
          ret = 1;
 
179
        }
 
180
      else if (b_state != state) /* a is discharging */
 
181
        {
 
182
          ret = -1;
 
183
        }
 
184
      else /* both are discharging; least-time-left goes first */
 
185
        {
 
186
          if (!a_time || !b_time) /* known time always trumps unknown time */
 
187
            ret = a_time ? -1 : 1;
 
188
          else if (a_time != b_time)
 
189
            ret = a_time < b_time ? -1 : 1;
 
190
          else
 
191
            ret = a_percentage < b_percentage ? -1 : 1;
 
192
        }
 
193
    }
 
194
 
 
195
  state = UP_DEVICE_STATE_CHARGING;
 
196
  if (!ret && (((a_state == state) && a_time) ||
 
197
               ((b_state == state) && b_time)))
 
198
    {
 
199
      if (a_state != state) /* b is charging */
 
200
        {
 
201
          ret = 1;
 
202
        }
 
203
      else if (b_state != state) /* a is charging */
 
204
        {
 
205
          ret = -1;
 
206
        }
 
207
      else /* both are discharging; most-time-to-charge goes first */
 
208
        {
 
209
          if (!a_time || !b_time) /* known time always trumps unknown time */
 
210
            ret = a_time ? -1 : 1;
 
211
          else if (a_time != b_time)
 
212
            ret = a_time > b_time ? -1 : 1;
 
213
          else
 
214
            ret = a_percentage < b_percentage ? -1 : 1;
 
215
        }
 
216
    }
 
217
 
 
218
  if (!ret) /* neither device is charging nor discharging... */
 
219
    {
 
220
      const int weight_a = get_device_kind_weight (a);
 
221
      const int weight_b = get_device_kind_weight (b);
 
222
 
 
223
      if (weight_a > weight_b)
 
224
        {
 
225
          ret = -1;
 
226
        }
 
227
      else if (weight_a < weight_b)
 
228
        {
 
229
          ret = 1;
 
230
        }
 
231
    }
 
232
 
 
233
  if (!ret)
 
234
    ret = a_state - b_state;
 
235
 
 
236
  return ret;
 
237
}
 
238
 
 
239
/***
 
240
****
 
241
****  HEADER SECTION
 
242
****
 
243
***/
 
244
 
 
245
static void
 
246
count_batteries (GList * devices, int *total, int *inuse)
 
247
{
 
248
  GList * l;
 
249
 
 
250
  for (l=devices; l!=NULL; l=l->next)
 
251
    {
 
252
      const IndicatorPowerDevice * device = INDICATOR_POWER_DEVICE(l->data);
 
253
 
 
254
      if (indicator_power_device_get_kind(device) == UP_DEVICE_KIND_BATTERY ||
 
255
          indicator_power_device_get_kind(device) == UP_DEVICE_KIND_UPS)
 
256
        {
 
257
          ++*total;
 
258
 
 
259
          const UpDeviceState state = indicator_power_device_get_state (device);
 
260
          if ((state == UP_DEVICE_STATE_CHARGING) ||
 
261
              (state == UP_DEVICE_STATE_DISCHARGING))
 
262
            ++*inuse;
 
263
        }
 
264
    }
 
265
 
 
266
  g_debug ("count_batteries found %d batteries (%d are charging/discharging)",
 
267
           *total, *inuse);
 
268
}
 
269
 
 
270
static gboolean
 
271
should_be_visible (IndicatorPowerService * self)
 
272
{
 
273
  gboolean visible = TRUE;
 
274
  priv_t * p = self->priv;
 
275
 
 
276
  const int policy = g_settings_get_enum (p->settings, SETTINGS_ICON_POLICY_S);
 
277
  g_debug ("policy is: %d (present==0, charge==1, never==2)", policy);
 
278
 
 
279
  if (policy == POWER_INDICATOR_ICON_POLICY_NEVER)
 
280
    {
 
281
      visible = FALSE;
 
282
    }
 
283
    else
 
284
    {
 
285
      int batteries=0, inuse=0;
 
286
 
 
287
      count_batteries (p->devices, &batteries, &inuse);
 
288
 
 
289
      if (policy == POWER_INDICATOR_ICON_POLICY_PRESENT)
 
290
        {
 
291
          visible = batteries > 0;
 
292
        }
 
293
      else if (policy == POWER_INDICATOR_ICON_POLICY_CHARGE)
 
294
        {
 
295
          visible = inuse > 0;
 
296
        }
 
297
    }
 
298
 
 
299
  g_debug ("should_be_visible: %s", visible?"yes":"no");
 
300
  return visible;
 
301
}
 
302
 
 
303
static GVariant *
 
304
create_header_state (IndicatorPowerService * self)
 
305
{
 
306
  GVariantBuilder b;
 
307
  gchar * label = NULL;
 
308
  gchar * a11y = NULL;
 
309
  GIcon * icon = NULL;
 
310
  priv_t * p = self->priv;
 
311
 
 
312
  if (p->primary_device != NULL)
 
313
    {
 
314
      gchar * details;
 
315
 
 
316
      indicator_power_device_get_time_details (p->primary_device,
 
317
                                               &label,
 
318
                                               &details,
 
319
                                               &a11y);
 
320
 
 
321
      icon = indicator_power_device_get_gicon (p->primary_device);
 
322
 
 
323
      g_free (details);
 
324
    }
 
325
 
 
326
  g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
 
327
 
 
328
  g_variant_builder_add (&b, "{sv}", "visible",
 
329
                         g_variant_new_boolean (should_be_visible (self)));
 
330
 
 
331
  if (label != NULL)
 
332
    {
 
333
      if (g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S))
 
334
        g_variant_builder_add (&b, "{sv}", "label",
 
335
                               g_variant_new_string (label));
 
336
 
 
337
      g_free (label);
 
338
    }
 
339
 
 
340
  if (icon != NULL)
 
341
    {
 
342
      g_variant_builder_add (&b, "{sv}", "icon", g_icon_serialize (icon));
 
343
 
 
344
      g_object_unref (icon);
 
345
    }
 
346
 
 
347
  if (a11y != NULL)
 
348
    {
 
349
      g_variant_builder_add (&b, "{sv}", "accessible-desc",
 
350
                             g_variant_new_string (a11y));
 
351
 
 
352
      g_free (a11y);
 
353
    }
 
354
 
 
355
  return g_variant_builder_end (&b);
 
356
}
 
357
 
 
358
 
 
359
/***
 
360
****
 
361
****  DEVICE SECTION
 
362
****
 
363
***/
 
364
 
 
365
static void
 
366
append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device)
 
367
{
 
368
  const UpDeviceKind kind = indicator_power_device_get_kind (device);
 
369
 
 
370
  if (kind != UP_DEVICE_KIND_LINE_POWER)
 
371
  {
 
372
    char * brief;
 
373
    char * label;
 
374
    char * a11y;
 
375
    GMenuItem * menu_item;
 
376
    GIcon * icon = indicator_power_device_get_gicon (device);
 
377
 
 
378
    indicator_power_device_get_time_details (device,
 
379
                                             &brief,
 
380
                                             &label,
 
381
                                             &a11y);
 
382
 
 
383
    menu_item = g_menu_item_new (label, "indicator.activate-statistics");
 
384
 
 
385
    if (icon != NULL)
 
386
      {
 
387
        g_menu_item_set_attribute_value (menu_item,
 
388
                                         G_MENU_ATTRIBUTE_ICON,
 
389
                                         g_icon_serialize (icon));
 
390
      }
 
391
 
 
392
    g_menu_append_item (menu, menu_item);
 
393
    g_object_unref (menu_item);
 
394
 
 
395
    g_clear_object (&icon);
 
396
    g_free (brief);
 
397
    g_free (label);
 
398
    g_free (a11y);
 
399
  }
 
400
}
 
401
 
 
402
 
 
403
static GMenuModel *
 
404
create_devices_section (IndicatorPowerService * self)
 
405
{
 
406
  GList * l;
 
407
  GMenu * menu = g_menu_new ();
 
408
 
 
409
  for (l=self->priv->devices; l!=NULL; l=l->next)
 
410
    append_device_to_menu (menu, l->data);
 
411
 
 
412
  return G_MENU_MODEL (menu);
 
413
}
 
414
 
 
415
 
 
416
/***
 
417
****
 
418
****  SETTINGS SECTION
 
419
****
 
420
***/
 
421
 
 
422
static GMenuModel *
 
423
create_settings_section (IndicatorPowerService * self G_GNUC_UNUSED)
 
424
{
 
425
  GMenu * menu = g_menu_new ();
 
426
 
 
427
  g_menu_append (menu,
 
428
                 _("Show Time in Menu Bar"),
 
429
                 "indicator.show-time");
 
430
 
 
431
  g_menu_append (menu,
 
432
                 _("Power Settings\342\200\246"),
 
433
                 "indicator.activate-settings");
 
434
 
 
435
  return G_MENU_MODEL (menu);
 
436
}
 
437
 
 
438
/***
 
439
****
 
440
****  SECTION REBUILDING
 
441
****
 
442
***/
 
443
 
 
444
/**
 
445
 * A small helper function for rebuild_now().
 
446
 * - removes the previous section
 
447
 * - adds and unrefs the new section
 
448
 */
 
449
static void
 
450
rebuild_section (GMenu * parent, int pos, GMenuModel * new_section)
 
451
{
 
452
  g_menu_remove (parent, pos);
 
453
  g_menu_insert_section (parent, pos, NULL, new_section);
 
454
  g_object_unref (new_section);
 
455
}
 
456
 
 
457
static void
 
458
rebuild_now (IndicatorPowerService * self, guint sections)
 
459
{
 
460
  priv_t * p = self->priv;
 
461
  struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP];
 
462
  struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER];
 
463
 
 
464
  if (p->conn == NULL) /* we haven't built the menus yet */
 
465
    return;
 
466
 
 
467
  if (sections & SECTION_HEADER)
 
468
    {
 
469
      g_simple_action_set_state (p->header_action, create_header_state (self));
 
470
    }
 
471
 
 
472
  if (sections & SECTION_DEVICES)
 
473
    {
 
474
      rebuild_section (desktop->submenu, 0, create_devices_section (self));
 
475
      rebuild_section (greeter->submenu, 0, create_devices_section (self));
 
476
    }
 
477
 
 
478
  if (sections & SECTION_SETTINGS)
 
479
    {
 
480
      rebuild_section (desktop->submenu, 1, create_settings_section (self));
 
481
    }
 
482
}
 
483
 
 
484
static inline void
 
485
rebuild_header_now (IndicatorPowerService * self)
 
486
{
 
487
  rebuild_now (self, SECTION_HEADER);
 
488
}
 
489
 
 
490
static inline void
 
491
rebuild_devices_section_now (IndicatorPowerService * self)
 
492
{
 
493
  rebuild_now (self, SECTION_DEVICES);
 
494
}
 
495
 
 
496
static inline void
 
497
rebuild_settings_section_now (IndicatorPowerService * self)
 
498
{
 
499
  rebuild_now (self, SECTION_SETTINGS);
 
500
}
 
501
 
 
502
static void
 
503
create_menu (IndicatorPowerService * self, int profile)
 
504
{
 
505
  GMenu * menu;
 
506
  GMenu * submenu;
 
507
  GMenuItem * header;
 
508
  GMenuModel * sections[16];
 
509
  guint i;
 
510
  guint n = 0;
 
511
 
 
512
  g_assert (0<=profile && profile<N_PROFILES);
 
513
  g_assert (self->priv->menus[profile].menu == NULL);
 
514
 
 
515
  if (profile == PROFILE_DESKTOP)
 
516
    {
 
517
      sections[n++] = create_devices_section (self);
 
518
      sections[n++] = create_settings_section (self);
 
519
    }
 
520
  else if (profile == PROFILE_GREETER)
 
521
    {
 
522
      sections[n++] = create_devices_section (self);
 
523
    }
 
524
 
 
525
  /* add sections to the submenu */
 
526
 
 
527
  submenu = g_menu_new ();
 
528
 
 
529
  for (i=0; i<n; ++i)
 
530
    {
 
531
      g_menu_append_section (submenu, NULL, sections[i]);
 
532
      g_object_unref (sections[i]);
 
533
    }
 
534
 
 
535
  /* add submenu to the header */
 
536
  header = g_menu_item_new (NULL, "indicator._header");
 
537
  g_menu_item_set_attribute (header, "x-canonical-type",
 
538
                             "s", "com.canonical.indicator.root");
 
539
  g_menu_item_set_submenu (header, G_MENU_MODEL (submenu));
 
540
  g_object_unref (submenu);
 
541
 
 
542
  /* add header to the menu */
 
543
  menu = g_menu_new ();
 
544
  g_menu_append_item (menu, header);
 
545
  g_object_unref (header);
 
546
 
 
547
  self->priv->menus[profile].menu = menu;
 
548
  self->priv->menus[profile].submenu = submenu;
 
549
}
 
550
 
 
551
/***
 
552
****  GActions
 
553
***/
 
554
 
 
555
/* Run a particular program based on an activation */
 
556
static void
 
557
execute_command (const gchar * cmd)
 
558
{
 
559
  GError * err = NULL;
 
560
 
 
561
  g_debug ("Issuing command '%s'", cmd);
 
562
 
 
563
  if (!g_spawn_command_line_async (cmd, &err))
 
564
    {
 
565
      g_warning ("Unable to start %s: %s", cmd, err->message);
 
566
      g_error_free (err);
 
567
    }
 
568
}
 
569
 
 
570
static void
 
571
on_settings_activated (GSimpleAction * a      G_GNUC_UNUSED,
 
572
                       GVariant      * param  G_GNUC_UNUSED,
 
573
                       gpointer        gself  G_GNUC_UNUSED)
 
574
{
 
575
  execute_command ("gnome-control-center power");
 
576
}
 
577
 
 
578
static void
 
579
on_statistics_activated (GSimpleAction * a      G_GNUC_UNUSED,
 
580
                         GVariant      * param  G_GNUC_UNUSED,
 
581
                         gpointer        gself  G_GNUC_UNUSED)
 
582
{
 
583
  execute_command ("gnome-power-statistics");
 
584
}
 
585
 
 
586
/* FIXME: use a GBinding to tie the gaction's state and the GSetting together? */
 
587
 
 
588
static void
 
589
set_show_time_flag (IndicatorPowerService * self, gboolean b)
 
590
{
 
591
  GVariant * v;
 
592
  priv_t * p = self->priv;
 
593
 
 
594
  /* update the settings */
 
595
  if (b != g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S))
 
596
    g_settings_set_boolean (p->settings, SETTINGS_SHOW_TIME_S, b);
 
597
 
 
598
  /* update the action state */
 
599
  v = g_action_get_state (G_ACTION(p->show_time_action));
 
600
  if (b != g_variant_get_boolean (v))
 
601
    g_simple_action_set_state (p->show_time_action, g_variant_new_boolean (b));
 
602
  g_variant_unref (v);
 
603
 
 
604
  rebuild_header_now (self);
 
605
}
 
606
static void
 
607
on_show_time_setting_changed (GSettings * settings, gchar * key, gpointer gself)
 
608
{
 
609
  set_show_time_flag (INDICATOR_POWER_SERVICE(gself),
 
610
                      g_settings_get_boolean (settings, key));
 
611
}
 
612
 
 
613
static void
 
614
on_show_time_action_state_changed (GAction    * action,
 
615
                                   GParamSpec * pspec   G_GNUC_UNUSED,
 
616
                                   gpointer     gself)
 
617
{
 
618
  GVariant * v = g_action_get_state (action);
 
619
  set_show_time_flag (INDICATOR_POWER_SERVICE(gself),
 
620
                      g_variant_get_boolean (v));
 
621
  g_variant_unref (v);
 
622
}
 
623
 
 
624
/* toggles the state */
 
625
static void
 
626
on_show_time_action_activated (GSimpleAction * simple,
 
627
                               GVariant      * parameter G_GNUC_UNUSED,
 
628
                               gpointer        unused    G_GNUC_UNUSED)
 
629
{
 
630
  GVariant * v = g_action_get_state (G_ACTION (simple));
 
631
  gboolean flag = g_variant_get_boolean (v);
 
632
  g_simple_action_set_state (simple, g_variant_new_boolean (!flag));
 
633
  g_variant_unref (v);
 
634
}
 
635
 
 
636
static void
 
637
init_gactions (IndicatorPowerService * self)
 
638
{
 
639
  GSimpleAction * a;
 
640
  gboolean show_time;
 
641
  priv_t * p = self->priv;
 
642
 
 
643
  GActionEntry entries[] = {
 
644
    { "activate-settings", on_settings_activated },
 
645
    { "activate-statistics", on_statistics_activated }
 
646
  };
 
647
 
 
648
  p->actions = g_simple_action_group_new ();
 
649
 
 
650
  g_action_map_add_action_entries (G_ACTION_MAP(p->actions),
 
651
                                   entries,
 
652
                                   G_N_ELEMENTS(entries),
 
653
                                   self);
 
654
 
 
655
  /* add the header action */
 
656
  a = g_simple_action_new_stateful ("_header", NULL, create_header_state (self));
 
657
  g_simple_action_group_insert (p->actions, G_ACTION(a));
 
658
  p->header_action = a;
 
659
 
 
660
  /* add the show-time action */
 
661
  show_time = g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S);
 
662
  a = g_simple_action_new_stateful ("show-time",
 
663
                                    NULL,
 
664
                                    g_variant_new_boolean(show_time));
 
665
  g_signal_connect (a, "activate",
 
666
                    G_CALLBACK(on_show_time_action_activated), self);
 
667
  g_signal_connect (a, "notify",
 
668
                    G_CALLBACK(on_show_time_action_state_changed), self);
 
669
  g_simple_action_group_insert (p->actions, G_ACTION(a));
 
670
  p->show_time_action = a;
 
671
 
 
672
  rebuild_header_now (self);
 
673
}
 
674
 
 
675
/***
 
676
****  GDBus Name Ownership & Menu / Action Exporting
 
677
***/
 
678
 
 
679
static void
 
680
on_bus_acquired (GDBusConnection * connection,
 
681
                 const gchar     * name,
 
682
                 gpointer          gself)
 
683
{
 
684
  int i;
 
685
  guint id;
 
686
  GError * err = NULL;
 
687
  IndicatorPowerService * self = INDICATOR_POWER_SERVICE(gself);
 
688
  priv_t * p = self->priv;
 
689
  GString * path = g_string_new (NULL);
 
690
 
 
691
  g_debug ("bus acquired: %s", name);
 
692
 
 
693
  p->conn = g_object_ref (G_OBJECT (connection));
 
694
 
 
695
  /* export the actions */
 
696
  if ((id = g_dbus_connection_export_action_group (connection,
 
697
                                                   BUS_PATH,
 
698
                                                   G_ACTION_GROUP (p->actions),
 
699
                                                   &err)))
 
700
    {
 
701
      p->actions_export_id = id;
 
702
    }
 
703
  else
 
704
    {
 
705
      g_warning ("cannot export action group: %s", err->message);
 
706
      g_clear_error (&err);
 
707
    }
 
708
 
 
709
  /* export the menus */
 
710
  for (i=0; i<N_PROFILES; ++i)
 
711
    {
 
712
      struct ProfileMenuInfo * menu = &p->menus[i];
 
713
 
 
714
      g_string_printf (path, "%s/%s", BUS_PATH, menu_names[i]);
 
715
 
 
716
      if (menu->menu == NULL)
 
717
        create_menu (self, i);
 
718
 
 
719
      if ((id = g_dbus_connection_export_menu_model (connection,
 
720
                                                     path->str,
 
721
                                                     G_MENU_MODEL (menu->menu),
 
722
                                                     &err)))
 
723
        {
 
724
          menu->export_id = id;
 
725
        }
 
726
      else
 
727
        {
 
728
          g_warning ("cannot export %s menu: %s", path->str, err->message);
 
729
          g_clear_error (&err);
 
730
        }
 
731
    }
 
732
 
 
733
  g_string_free (path, TRUE);
 
734
}
 
735
 
 
736
static void
 
737
unexport (IndicatorPowerService * self)
 
738
{
 
739
  int i;
 
740
  priv_t * p = self->priv;
 
741
 
 
742
  /* unexport the menus */
 
743
  for (i=0; i<N_PROFILES; ++i)
 
744
    {
 
745
      guint * id = &self->priv->menus[i].export_id;
 
746
 
 
747
      if (*id)
 
748
        {
 
749
          g_dbus_connection_unexport_menu_model (p->conn, *id);
 
750
          *id = 0;
 
751
        }
 
752
    }
 
753
 
 
754
  /* unexport the actions */
 
755
  if (p->actions_export_id)
 
756
    {
 
757
      g_dbus_connection_unexport_action_group (p->conn, p->actions_export_id);
 
758
      p->actions_export_id = 0;
 
759
    }
 
760
}
 
761
 
 
762
static void
 
763
on_name_lost (GDBusConnection * connection G_GNUC_UNUSED,
 
764
              const gchar     * name,
 
765
              gpointer          gself)
 
766
{
 
767
  IndicatorPowerService * self = INDICATOR_POWER_SERVICE (gself);
 
768
 
 
769
  g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, name);
 
770
 
 
771
  unexport (self);
 
772
 
 
773
  g_signal_emit (self, signals[SIGNAL_NAME_LOST], 0, NULL);
 
774
}
 
775
 
 
776
/***
 
777
****  Events
 
778
***/
 
779
 
 
780
static void
 
781
on_devices_changed (IndicatorPowerService * self)
 
782
{
 
783
  priv_t * p = self->priv;
 
784
 
 
785
  /* update the device list */
 
786
  g_list_free_full (p->devices, (GDestroyNotify)g_object_unref);
 
787
  p->devices = indicator_power_device_provider_get_devices (p->device_provider);
 
788
 
 
789
  /* update the primary device */
 
790
  g_clear_object (&p->primary_device);
 
791
  p->primary_device = indicator_power_service_choose_primary_device (p->devices);
 
792
 
 
793
  rebuild_now (self, SECTION_HEADER | SECTION_DEVICES);
 
794
}
 
795
 
 
796
 
 
797
/***
 
798
****  GObject virtual functions
 
799
***/
 
800
 
 
801
static void
 
802
my_get_property (GObject     * o,
 
803
                  guint         property_id,
 
804
                  GValue      * value,
 
805
                  GParamSpec  * pspec)
 
806
{
 
807
  IndicatorPowerService * self = INDICATOR_POWER_SERVICE (o);
 
808
  priv_t * p = self->priv;
 
809
 
 
810
  switch (property_id)
 
811
    {
 
812
      case PROP_DEVICE_PROVIDER:
 
813
        g_value_set_object (value, p->device_provider);
 
814
        break;
 
815
 
 
816
      default:
 
817
        G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
 
818
    }
 
819
}
 
820
 
 
821
static void
 
822
my_set_property (GObject       * o,
 
823
                 guint           property_id,
 
824
                 const GValue  * value,
 
825
                 GParamSpec    * pspec)
 
826
{
 
827
  IndicatorPowerService * self = INDICATOR_POWER_SERVICE (o);
 
828
 
 
829
  switch (property_id)
 
830
    {
 
831
      case PROP_DEVICE_PROVIDER:
 
832
        indicator_power_service_set_device_provider (self, g_value_get_object (value));
 
833
        break;
 
834
 
 
835
      default:
 
836
        G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
 
837
    }
 
838
}
 
839
 
 
840
static void
 
841
my_dispose (GObject * o)
 
842
{
 
843
  IndicatorPowerService * self = INDICATOR_POWER_SERVICE(o);
 
844
  priv_t * p = self->priv;
 
845
 
 
846
  if (p->own_id)
 
847
    {
 
848
      g_bus_unown_name (p->own_id);
 
849
      p->own_id = 0;
 
850
    }
 
851
 
 
852
  unexport (self);
 
853
 
 
854
  if (p->cancellable != NULL)
 
855
    {
 
856
      g_cancellable_cancel (p->cancellable);
 
857
      g_clear_object (&p->cancellable);
 
858
    }
 
859
 
 
860
  if (p->settings != NULL)
 
861
    {
 
862
      g_signal_handlers_disconnect_by_data (p->settings, self);
 
863
 
 
864
      g_clear_object (&p->settings);
 
865
    }
 
866
 
 
867
  if (p->show_time_action != NULL)
 
868
    {
 
869
      g_signal_handlers_disconnect_by_data (p->show_time_action, self);
 
870
 
 
871
      g_clear_object (&p->show_time_action);
 
872
    }
 
873
 
 
874
  g_clear_object (&p->header_action);
 
875
  g_clear_object (&p->show_time_action);
 
876
  g_clear_object (&p->actions);
 
877
 
 
878
  g_clear_object (&p->conn);
 
879
 
 
880
  indicator_power_service_set_device_provider (self, NULL);
 
881
 
 
882
  G_OBJECT_CLASS (indicator_power_service_parent_class)->dispose (o);
 
883
}
 
884
 
 
885
/***
 
886
****  Instantiation
 
887
***/
 
888
 
 
889
static void
 
890
indicator_power_service_init (IndicatorPowerService * self)
 
891
{
 
892
  priv_t * p = G_TYPE_INSTANCE_GET_PRIVATE (self,
 
893
                                            INDICATOR_TYPE_POWER_SERVICE,
 
894
                                            IndicatorPowerServicePrivate);
 
895
  self->priv = p;
 
896
 
 
897
  p->cancellable = g_cancellable_new ();
 
898
 
 
899
  p->settings = g_settings_new ("com.canonical.indicator.power");
 
900
 
 
901
  init_gactions (self);
 
902
 
 
903
  g_signal_connect_swapped (p->settings, "changed::" SETTINGS_ICON_POLICY_S,
 
904
                            G_CALLBACK(rebuild_header_now), self);
 
905
  g_signal_connect (p->settings, "changed::" SETTINGS_SHOW_TIME_S,
 
906
                    G_CALLBACK(on_show_time_setting_changed), self);
 
907
 
 
908
  p->own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
 
909
                              BUS_NAME,
 
910
                              G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
 
911
                              on_bus_acquired,
 
912
                              NULL,
 
913
                              on_name_lost,
 
914
                              self,
 
915
                              NULL);
 
916
}
 
917
 
 
918
static void
 
919
indicator_power_service_class_init (IndicatorPowerServiceClass * klass)
 
920
{
 
921
  GObjectClass * object_class = G_OBJECT_CLASS (klass);
 
922
 
 
923
  object_class->dispose = my_dispose;
 
924
  object_class->get_property = my_get_property;
 
925
  object_class->set_property = my_set_property;
 
926
 
 
927
  g_type_class_add_private (klass, sizeof (IndicatorPowerServicePrivate));
 
928
 
 
929
  signals[SIGNAL_NAME_LOST] = g_signal_new (
 
930
    INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST,
 
931
    G_TYPE_FROM_CLASS(klass),
 
932
    G_SIGNAL_RUN_LAST,
 
933
    G_STRUCT_OFFSET (IndicatorPowerServiceClass, name_lost),
 
934
    NULL, NULL,
 
935
    g_cclosure_marshal_VOID__VOID,
 
936
    G_TYPE_NONE, 0);
 
937
 
 
938
  properties[PROP_0] = NULL;
 
939
 
 
940
  properties[PROP_DEVICE_PROVIDER] = g_param_spec_object (
 
941
    "device-provider",
 
942
    "Device Provider",
 
943
    "Source for power devices",
 
944
    G_TYPE_OBJECT,
 
945
    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
946
 
 
947
  g_object_class_install_properties (object_class, LAST_PROP, properties);
 
948
}
 
949
 
 
950
/***
 
951
****  Public API
 
952
***/
 
953
 
 
954
IndicatorPowerService *
 
955
indicator_power_service_new (IndicatorPowerDeviceProvider * device_provider)
 
956
{
 
957
  GObject * o = g_object_new (INDICATOR_TYPE_POWER_SERVICE,
 
958
                              "device-provider", device_provider,
 
959
                              NULL);
 
960
 
 
961
  return INDICATOR_POWER_SERVICE (o);
 
962
}
 
963
 
 
964
void
 
965
indicator_power_service_set_device_provider (IndicatorPowerService * self,
 
966
                                             IndicatorPowerDeviceProvider * dp)
 
967
{
 
968
  priv_t * p;
 
969
 
 
970
  g_return_if_fail (INDICATOR_IS_POWER_SERVICE (self));
 
971
  g_return_if_fail (!dp || INDICATOR_IS_POWER_DEVICE_PROVIDER (dp));
 
972
  p = self->priv;
 
973
 
 
974
  if (p->device_provider != NULL)
 
975
    {
 
976
      g_signal_handlers_disconnect_by_func (p->device_provider,
 
977
                                            G_CALLBACK(on_devices_changed),
 
978
                                            self);
 
979
      g_clear_object (&p->device_provider);
 
980
 
 
981
      g_clear_object (&p->primary_device);
 
982
 
 
983
      g_list_free_full (p->devices, g_object_unref);
 
984
      p->devices = NULL;
 
985
    }
 
986
 
 
987
  if (dp != NULL)
 
988
    {
 
989
      p->device_provider = g_object_ref (dp);
 
990
 
 
991
      g_signal_connect_swapped (p->device_provider, "devices-changed",
 
992
                                G_CALLBACK(on_devices_changed), self);
 
993
 
 
994
      on_devices_changed (self);
 
995
    }
 
996
}
 
997
 
 
998
IndicatorPowerDevice *
 
999
indicator_power_service_choose_primary_device (GList * devices)
 
1000
{
 
1001
  IndicatorPowerDevice * primary = NULL;
 
1002
 
 
1003
  if (devices != NULL)
 
1004
    {
 
1005
      GList * tmp;
 
1006
 
 
1007
      tmp = g_list_copy (devices);
 
1008
      tmp = g_list_sort (tmp, device_compare_func);
 
1009
      primary = g_object_ref (tmp->data);
 
1010
 
 
1011
      g_list_free (tmp);
 
1012
    }
 
1013
 
 
1014
  return primary;
 
1015
}