~ubuntu-branches/ubuntu/vivid/alarm-clock-applet/vivid

« back to all changes in this revision

Viewing changes to src/alarm-applet.c

  • Committer: Bazaar Package Importer
  • Author(s): Chow Loong Jin
  • Date: 2009-05-30 23:24:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090530232427-88on1j2ily4ajxdz
Tags: upstream-0.2.6
ImportĀ upstreamĀ versionĀ 0.2.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * alarm-applet.c -- Alarm Clock applet bootstrap
 
3
 *
 
4
 * Copyright (C) 2007-2008 Johannes H. Jensen <joh@pseudoberries.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License
 
8
 * as published by the Free Software Foundation; either version 2
 
9
 * of the License, or (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
19
 *
 
20
 * Authors:
 
21
 *              Johannes H. Jensen <joh@pseudoberries.com>
 
22
 */
 
23
 
 
24
#include "alarm-applet.h"
 
25
 
 
26
#include "alarm.h"
 
27
#include "edit-alarm.h"
 
28
 
 
29
/*
 
30
 * DEFINTIIONS {{
 
31
 */
 
32
 
 
33
gchar *supported_sound_mime_types [] = { "audio", "video", "application/ogg", NULL };
 
34
GHashTable *app_command_map = NULL;
 
35
 
 
36
/*
 
37
 * }} DEFINTIIONS
 
38
 */
 
39
 
 
40
/*
 
41
 * Snooze any running (read: playing sound) alarms.
 
42
 */
 
43
void
 
44
alarm_applet_snooze_alarms (AlarmApplet *applet)
 
45
{
 
46
        GList *l;
 
47
        Alarm *a;
 
48
 
 
49
        g_debug ("Snoozing alarms...");
 
50
 
 
51
        // Loop through alarms and snooze all of 'em
 
52
        for (l = applet->alarms; l; l = l->next) {
 
53
                a = ALARM (l->data);
 
54
                alarm_snooze (a);
 
55
        }
 
56
 
 
57
        // Close notification if present.
 
58
#ifdef HAVE_LIBNOTIFY
 
59
        if (applet->notify) {
 
60
                alarm_applet_notification_close (applet);
 
61
        }
 
62
#endif
 
63
}
 
64
 
 
65
/*
 
66
 * Clear any running (read: playing sound) alarms.
 
67
 */
 
68
void
 
69
alarm_applet_clear_alarms (AlarmApplet *applet)
 
70
{
 
71
        GList *l;
 
72
        Alarm *a;
 
73
 
 
74
        g_debug ("Clearing alarms...");
 
75
 
 
76
        // Loop through alarms and clear all of 'em
 
77
        for (l = applet->alarms; l; l = l->next) {
 
78
                a = ALARM (l->data);
 
79
                alarm_clear (a);
 
80
        }
 
81
 
 
82
        // Close notification if present.
 
83
#ifdef HAVE_LIBNOTIFY
 
84
        if (applet->notify) {
 
85
                alarm_applet_notification_close (applet);
 
86
        }
 
87
#endif
 
88
}
 
89
 
 
90
 
 
91
/*
 
92
 * Sounds list {{
 
93
 */
 
94
 
 
95
// Load sounds into list
 
96
// TODO: Refactor to use a GHashTable with string hash
 
97
void
 
98
alarm_applet_sounds_load (AlarmApplet *applet)
 
99
{
 
100
        Alarm *alarm;
 
101
        AlarmListEntry *entry;
 
102
        GList *l, *l2;
 
103
        gboolean found;
 
104
 
 
105
        //g_assert (applet->alarms);
 
106
 
 
107
        // Free old list
 
108
        if (applet->sounds != NULL)
 
109
                alarm_list_entry_list_free (&(applet->sounds));
 
110
 
 
111
        // Load stock sounds
 
112
        applet->sounds = alarm_list_entry_list_new ("file://" ALARM_SOUNDSDIR,
 
113
                                                                                                supported_sound_mime_types);
 
114
 
 
115
        // Load custom sounds from alarms
 
116
        for (l = applet->alarms; l != NULL; l = l->next) {
 
117
                alarm = ALARM (l->data);
 
118
                found = FALSE;
 
119
                for (l2 = applet->sounds; l2 != NULL; l2 = l2->next) {
 
120
                        entry = (AlarmListEntry *)l2->data;
 
121
                        if (strcmp (alarm->sound_file, entry->data) == 0) {
 
122
                                // FOUND
 
123
                                found = TRUE;
 
124
                                break;
 
125
                        }
 
126
                }
 
127
 
 
128
                if (!found) {
 
129
                        // Add to list
 
130
                        entry = alarm_list_entry_new_file (alarm->sound_file, NULL, NULL);
 
131
                        if (entry) {
 
132
                                applet->sounds = g_list_append (applet->sounds, entry);
 
133
                        }
 
134
                }
 
135
        }
 
136
}
 
137
 
 
138
// Notify callback for changes to an alarm's sound_file
 
139
static void
 
140
alarm_sound_file_changed (GObject *object,
 
141
                                                  GParamSpec *param,
 
142
                                                  gpointer data)
 
143
{
 
144
        Alarm *alarm            = ALARM (object);
 
145
        AlarmApplet *applet = (AlarmApplet *)data;
 
146
 
 
147
        g_debug ("alarm_sound_file_changed: #%d", alarm->id);
 
148
 
 
149
        // Reload sounds list
 
150
        alarm_applet_sounds_load (applet);
 
151
}
 
152
 
 
153
 
 
154
/*
 
155
 * }} Sounds list
 
156
 */
 
157
 
 
158
 
 
159
/*
 
160
 * Apps list {{
 
161
 */
 
162
 
 
163
static gchar *
 
164
gnome_da_xml_get_string (const xmlNode *parent, const gchar *val_name)
 
165
{
 
166
    const gchar * const *sys_langs;
 
167
    xmlChar *node_lang;
 
168
    xmlNode *element;
 
169
    gchar *ret_val = NULL;
 
170
    xmlChar *xml_val_name;
 
171
    gint len;
 
172
    gint i;
 
173
 
 
174
    g_return_val_if_fail (parent != NULL, ret_val);
 
175
    g_return_val_if_fail (parent->children != NULL, ret_val);
 
176
    g_return_val_if_fail (val_name != NULL, ret_val);
 
177
 
 
178
#if GLIB_CHECK_VERSION (2, 6, 0)
 
179
    sys_langs = g_get_language_names ();
 
180
#endif
 
181
 
 
182
    xml_val_name = xmlCharStrdup (val_name);
 
183
    len = xmlStrlen (xml_val_name);
 
184
 
 
185
    for (element = parent->children; element != NULL; element = element->next) {
 
186
                if (!xmlStrncmp (element->name, xml_val_name, len)) {
 
187
                    node_lang = xmlNodeGetLang (element);
 
188
 
 
189
                    if (node_lang == NULL) {
 
190
                        ret_val = (gchar *) xmlNodeGetContent (element);
 
191
                    } else {
 
192
                                for (i = 0; sys_langs[i] != NULL; i++) {
 
193
                                    if (!strcmp (sys_langs[i], node_lang)) {
 
194
                                                ret_val = (gchar *) xmlNodeGetContent (element);
 
195
                                                /* since sys_langs is sorted from most desirable to
 
196
                                                 * least desirable, exit at first match
 
197
                                                 */
 
198
                                                break;
 
199
                                    }
 
200
                                }
 
201
                    }
 
202
                    xmlFree (node_lang);
 
203
                }
 
204
    }
 
205
 
 
206
    xmlFree (xml_val_name);
 
207
    return ret_val;
 
208
}
 
209
 
 
210
static const gchar *
 
211
get_app_command (const gchar *app)
 
212
{
 
213
        // TODO: Shouldn't be a global variable
 
214
        if (app_command_map == NULL) {
 
215
                app_command_map = g_hash_table_new (g_str_hash, g_str_equal);
 
216
 
 
217
                /* `rhythmbox-client --play' doesn't actually start playing unless
 
218
                 * Rhythmbox is already running. Sounds like a Bug. */
 
219
                g_hash_table_insert (app_command_map,
 
220
                                                         "rhythmbox", "rhythmbox-client --play");
 
221
 
 
222
                g_hash_table_insert (app_command_map,
 
223
                                                         "banshee", "banshee --play");
 
224
 
 
225
                // Note that totem should already be open with a file for this to work.
 
226
                g_hash_table_insert (app_command_map,
 
227
                                                         "totem", "totem --play");
 
228
 
 
229
                // Muine crashes and doesn't seem to have any play command
 
230
                /*g_hash_table_insert (app_command_map,
 
231
                                                         "muine", "muine");*/
 
232
        }
 
233
 
 
234
        return g_hash_table_lookup (app_command_map, app);
 
235
}
 
236
 
 
237
// Load stock apps into list
 
238
void
 
239
alarm_applet_apps_load (AlarmApplet *applet)
 
240
{
 
241
        AlarmListEntry *entry;
 
242
        gchar *filename, *name, *icon, *command;
 
243
        xmlDoc *xml_doc;
 
244
        xmlNode *root, *section, *element;
 
245
    gchar *executable;
 
246
    const gchar *tmp;
 
247
 
 
248
        if (applet->apps != NULL)
 
249
                alarm_list_entry_list_free (&(applet->apps));
 
250
 
 
251
        // We'll get the default media players from g-d-a.xml
 
252
        // from gnome-control-center
 
253
        filename = g_build_filename (ALARM_DATADIR,
 
254
                                                                 "gnome-control-center",
 
255
                                                                 "default-apps",
 
256
                                                                 "gnome-default-applications.xml",
 
257
                                                                 NULL);
 
258
 
 
259
        if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
 
260
                g_critical ("Could not find %s.", filename);
 
261
                return;
 
262
        }
 
263
 
 
264
    xml_doc = xmlParseFile (filename);
 
265
 
 
266
    if (!xml_doc) {
 
267
        g_warning ("Could not load %s.", filename);
 
268
        return;
 
269
    }
 
270
 
 
271
    root = xmlDocGetRootElement (xml_doc);
 
272
 
 
273
        for (section = root->children; section != NULL; section = section->next) {
 
274
                if (!xmlStrncmp (section->name, "media-players", 13)) {
 
275
                    for (element = section->children; element != NULL; element = element->next) {
 
276
                                if (!xmlStrncmp (element->name, "media-player", 12)) {
 
277
                                    executable = gnome_da_xml_get_string (element, "executable");
 
278
                                    if (is_executable_valid (executable)) {
 
279
                                        name = gnome_da_xml_get_string (element, "name");
 
280
                                        icon = gnome_da_xml_get_string (element, "icon-name");
 
281
 
 
282
                                        // Lookup executable in app command map
 
283
                                        tmp = get_app_command (executable);
 
284
                                        if (tmp)
 
285
                                                command = g_strdup (tmp);
 
286
                                        else {
 
287
                                                // Fall back to command specified in XML
 
288
                                                command = gnome_da_xml_get_string (element, "command");
 
289
                                        }
 
290
 
 
291
 
 
292
 
 
293
                                                g_debug ("LOAD-APPS: Adding '%s': %s [%s]", name, command, icon);
 
294
 
 
295
                                                entry = alarm_list_entry_new (name, command, icon);
 
296
 
 
297
                                                g_free (name);
 
298
                                                g_free (command);
 
299
                                                g_free (icon);
 
300
 
 
301
                                                applet->apps = g_list_append (applet->apps, entry);
 
302
                                    }
 
303
 
 
304
                                    if (executable)
 
305
                                        g_free (executable);
 
306
                                }
 
307
                    }
 
308
            }
 
309
        }
 
310
 
 
311
 
 
312
 
 
313
 
 
314
        g_free (filename);
 
315
 
 
316
//      entry = alarm_list_entry_new("Rhythmbox Music Player", "rhythmbox", "rhythmbox");
 
317
//      applet->apps = g_list_append (applet->apps, entry);
 
318
}
 
319
 
 
320
 
 
321
/*
 
322
 * Alarm signals {{
 
323
 */
 
324
 
 
325
/*
 
326
 * Find the soonest-to-trigger upcoming alarm
 
327
 */
 
328
static void
 
329
alarm_applet_upcoming_alarm_update (AlarmApplet *applet)
 
330
{
 
331
        GList *l;
 
332
        Alarm *a;
 
333
 
 
334
        applet->upcoming_alarm = NULL;
 
335
 
 
336
        /* Loop through alarms looking for all active upcoming alarms and locate
 
337
         * the one which will trigger sooner.
 
338
         */
 
339
 
 
340
        for (l = applet->alarms; l; l = l->next) {
 
341
                a = ALARM (l->data);
 
342
 
 
343
                if (!a->active) continue;
 
344
 
 
345
                if (!applet->upcoming_alarm || a->timestamp < applet->upcoming_alarm->timestamp) {
 
346
                        // This alarm is most recent
 
347
                        applet->upcoming_alarm = a;
 
348
                }
 
349
        }
 
350
}
 
351
 
 
352
/*
 
353
 * Callback for when an alarm is activated / deactivated.
 
354
 * We use this to update our closest upcoming alarm.
 
355
 */
 
356
static void
 
357
alarm_active_changed (GObject *object,
 
358
                                          GParamSpec *param,
 
359
                                          gpointer data)
 
360
{
 
361
        Alarm *alarm            = ALARM (object);
 
362
        AlarmApplet *applet = (AlarmApplet *)data;
 
363
 
 
364
        g_debug ("alarm_active_changed: #%d to %d", alarm->id, alarm->active);
 
365
 
 
366
        // Check if this was the upcoming alarm
 
367
        if (!alarm->active) {
 
368
                if (applet->upcoming_alarm == alarm) {
 
369
                        applet->upcoming_alarm = NULL;
 
370
 
 
371
                        alarm_applet_upcoming_alarm_update (applet);
 
372
                        alarm_applet_label_update (applet);
 
373
                        alarm_applet_icon_update (applet);
 
374
                }
 
375
 
 
376
                return;
 
377
        }
 
378
 
 
379
        if (!applet->upcoming_alarm || alarm->timestamp < applet->upcoming_alarm->timestamp) {
 
380
                // We're next!
 
381
                applet->upcoming_alarm = alarm;
 
382
 
 
383
                alarm_applet_label_update (applet);
 
384
 
 
385
                return;
 
386
        }
 
387
}
 
388
 
 
389
/*
 
390
 * Callback for when an alarm is triggered
 
391
 * We show the notification bubble here if appropriate.
 
392
 */
 
393
static void
 
394
alarm_triggered (Alarm *alarm, gpointer data)
 
395
{
 
396
        AlarmApplet *applet = (AlarmApplet *)data;
 
397
 
 
398
        g_debug ("Alarm triggered: #%d", alarm->id);
 
399
 
 
400
        if (alarm->notify_bubble) {
 
401
                g_debug ("Alarm #%d NOTIFICATION DISPLAY", alarm->id);
 
402
                alarm_applet_notification_display (applet, alarm);
 
403
        }
 
404
}
 
405
 
 
406
/*
 
407
 * }} Alarm signals
 
408
 */
 
409
 
 
410
/*
 
411
 * Alarms list {{
 
412
 */
 
413
 
 
414
// TODO: Refactor to use a GHashTable instead
 
415
void
 
416
alarm_applet_alarms_load (AlarmApplet *applet)
 
417
{
 
418
        if (applet->alarms != NULL) {
 
419
                GList *l;
 
420
 
 
421
                /* Free old alarm objects */
 
422
                for (l = applet->alarms; l != NULL; l = l->next) {
 
423
                        g_object_unref (ALARM (l->data));
 
424
                }
 
425
 
 
426
                /* Free list */
 
427
                g_list_free (applet->alarms);
 
428
        }
 
429
 
 
430
        /* Fetch list of alarms */
 
431
        applet->alarms = alarm_get_list (ALARM_GCONF_DIR);
 
432
}
 
433
 
 
434
void
 
435
alarm_applet_alarms_add (AlarmApplet *applet, Alarm *alarm)
 
436
{
 
437
        applet->alarms = g_list_append (applet->alarms, alarm);
 
438
 
 
439
        g_signal_connect (alarm, "notify::sound-file", G_CALLBACK (alarm_sound_file_changed), applet);
 
440
        g_signal_connect (alarm, "notify::active", G_CALLBACK (alarm_active_changed), applet);
 
441
        g_signal_connect (alarm, "notify::time", G_CALLBACK (alarm_active_changed), applet);
 
442
 
 
443
        g_signal_connect (alarm, "alarm", G_CALLBACK (alarm_triggered), applet);
 
444
}
 
445
 
 
446
void
 
447
alarm_applet_alarms_remove (AlarmApplet *applet, Alarm *alarm)
 
448
{
 
449
        /*
 
450
         * Remove from list
 
451
         */
 
452
        applet->alarms = g_list_remove (applet->alarms, alarm);
 
453
 
 
454
        /*
 
455
         * Clear list store. This will decrease the refcount of our alarms by 1.
 
456
         */
 
457
        if (applet->list_alarms_store)
 
458
                gtk_list_store_clear (applet->list_alarms_store);
 
459
 
 
460
        g_debug ("alarm_applet_alarms_remove (..., %p): refcount = %d", alarm, G_OBJECT (alarm)->ref_count);
 
461
 
 
462
        /*
 
463
         * If this is the upcoming alarm, update it.
 
464
         */
 
465
        if (applet->upcoming_alarm == alarm) {
 
466
                alarm_applet_upcoming_alarm_update (applet);
 
467
        }
 
468
 
 
469
        /*
 
470
         * Remove any signal handlers for this alarm instance.
 
471
         */
 
472
        g_signal_handlers_disconnect_matched (alarm, 0, 0, 0, NULL, NULL, NULL);
 
473
 
 
474
        /*
 
475
         * Dereference alarm
 
476
         */
 
477
        g_object_unref (alarm);
 
478
}
 
479
 
 
480
/*
 
481
 * }} Alarms list
 
482
 */
 
483
 
 
484
void
 
485
alarm_applet_destroy (AlarmApplet *applet)
 
486
{
 
487
        GList *l;
 
488
        Alarm *a;
 
489
        AlarmSettingsDialog *dialog;
 
490
 
 
491
        g_debug ("AlarmApplet DESTROY");
 
492
 
 
493
        // Remove any timers
 
494
        if (applet->timer_id) {
 
495
                g_source_remove (applet->timer_id);
 
496
        }
 
497
 
 
498
        // Destroy alarms list
 
499
        if (applet->list_alarms_dialog) {
 
500
                list_alarms_dialog_close (applet);
 
501
        }
 
502
 
 
503
        // Destroy preferences dialog
 
504
        if (applet->preferences_dialog) {
 
505
                gtk_widget_destroy (GTK_WIDGET (applet->preferences_dialog));
 
506
        }
 
507
 
 
508
        // Loop through all alarms and free like a mad man!
 
509
        for (l = applet->alarms; l; l = l->next) {
 
510
                a = ALARM (l->data);
 
511
 
 
512
                // Check if a dialog is open for this alarm
 
513
                dialog = (AlarmSettingsDialog *)g_hash_table_lookup (applet->edit_alarm_dialogs, (gconstpointer)a->id);
 
514
 
 
515
                if (dialog) {
 
516
                        alarm_settings_dialog_close (dialog);
 
517
                        //g_free (dialog);
 
518
                }
 
519
 
 
520
                g_object_unref (a);
 
521
        }
 
522
 
 
523
        // Remove sounds list
 
524
        if (applet->sounds) {
 
525
                alarm_list_entry_list_free(&(applet->sounds));
 
526
        }
 
527
 
 
528
        // Remove apps list
 
529
        if (applet->apps) {
 
530
                alarm_list_entry_list_free(&(applet->apps));
 
531
        }
 
532
 
 
533
        if (app_command_map) {
 
534
                g_hash_table_destroy (app_command_map);
 
535
                app_command_map = NULL;
 
536
        }
 
537
 
 
538
        // Free GConf dir
 
539
        g_free (applet->gconf_dir);
 
540
 
 
541
        // Finally free the AlarmApplet struct itself
 
542
        g_free (applet);
 
543
}
 
544
 
 
545
 
 
546
/*
 
547
 * INIT {{
 
548
 */
 
549
 
 
550
static gboolean
 
551
alarm_applet_factory (PanelApplet *panelapplet,
 
552
                                          const gchar *iid,
 
553
                                          gpointer data)
 
554
{
 
555
        AlarmApplet *applet;
 
556
 
 
557
        if (strcmp (iid, "OAFIID:AlarmClock") != 0)
 
558
                return FALSE;
 
559
 
 
560
        /* Initialize applet struct,
 
561
         * fill with zero's */
 
562
        applet = g_new0 (AlarmApplet, 1);
 
563
 
 
564
        applet->parent = panelapplet;
 
565
        applet->edit_alarm_dialogs = g_hash_table_new (NULL, NULL);
 
566
 
 
567
        /* Preferences (defaults).
 
568
         * ...gconf_get_string can return NULL if the key is not found. We can't
 
569
         * assume the schema provides the default values for strings. */
 
570
        applet->show_label = TRUE;
 
571
        applet->label_type = LABEL_TYPE_TIME;
 
572
 
 
573
        /* Prepare applet */
 
574
        panel_applet_add_preferences (applet->parent, ALARM_SCHEMA_DIR, NULL);
 
575
        panel_applet_set_flags (PANEL_APPLET (panelapplet), PANEL_APPLET_EXPAND_MINOR);
 
576
        applet->gconf_dir = panel_applet_get_preferences_key (applet->parent);
 
577
 
 
578
        /* Set up gconf handlers */
 
579
        alarm_applet_gconf_init (applet);
 
580
 
 
581
        /* Load gconf values */
 
582
        alarm_applet_gconf_load (applet);
 
583
 
 
584
        /* Load alarms */
 
585
        alarm_applet_alarms_load (applet);
 
586
 
 
587
        /* Find any upcoming active alarms */
 
588
        alarm_applet_upcoming_alarm_update (applet);
 
589
 
 
590
        /* Load sounds from alarms */
 
591
        alarm_applet_sounds_load (applet);
 
592
 
 
593
        /* Load apps for alarms */
 
594
        alarm_applet_apps_load (applet);
 
595
 
 
596
        /* Connect sound_file notify callback to all alarms */
 
597
        alarm_signal_connect_list (applet->alarms, "notify::sound-file",
 
598
                                                           G_CALLBACK (alarm_sound_file_changed), applet);
 
599
 
 
600
        /* Connect active & time notify callback to all alarms */
 
601
        alarm_signal_connect_list (applet->alarms, "notify::active",
 
602
                                                           G_CALLBACK (alarm_active_changed), applet);
 
603
        alarm_signal_connect_list (applet->alarms, "notify::time",
 
604
                                                           G_CALLBACK (alarm_active_changed), applet);
 
605
 
 
606
        /* Connect alarm trigger notify to all alarms */
 
607
        alarm_signal_connect_list (applet->alarms, "alarm",
 
608
                                                           G_CALLBACK (alarm_triggered), applet);
 
609
 
 
610
        /* Set up properties menu */
 
611
        alarm_applet_menu_init (applet);
 
612
 
 
613
        /* Set up applet */
 
614
        alarm_applet_ui_init (applet);
 
615
 
 
616
        return TRUE;
 
617
}
 
618
 
 
619
PANEL_APPLET_BONOBO_FACTORY ("OAFIID:AlarmClock_Factory",
 
620
                             PANEL_TYPE_APPLET,
 
621
                             "alarm_clock",
 
622
                             VERSION,
 
623
                             alarm_applet_factory,
 
624
                             NULL);
 
625
 
 
626
 
 
627
/*
 
628
 * }} INIT
 
629
 */