2
* alarm-applet.c -- Alarm Clock applet bootstrap
4
* Copyright (C) 2007-2008 Johannes H. Jensen <joh@pseudoberries.com>
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.
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.
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.
21
* Johannes H. Jensen <joh@pseudoberries.com>
24
#include "alarm-applet.h"
27
#include "edit-alarm.h"
33
gchar *supported_sound_mime_types [] = { "audio", "video", "application/ogg", NULL };
34
GHashTable *app_command_map = NULL;
41
* Snooze any running (read: playing sound) alarms.
44
alarm_applet_snooze_alarms (AlarmApplet *applet)
49
g_debug ("Snoozing alarms...");
51
// Loop through alarms and snooze all of 'em
52
for (l = applet->alarms; l; l = l->next) {
57
// Close notification if present.
60
alarm_applet_notification_close (applet);
66
* Clear any running (read: playing sound) alarms.
69
alarm_applet_clear_alarms (AlarmApplet *applet)
74
g_debug ("Clearing alarms...");
76
// Loop through alarms and clear all of 'em
77
for (l = applet->alarms; l; l = l->next) {
82
// Close notification if present.
85
alarm_applet_notification_close (applet);
95
// Load sounds into list
96
// TODO: Refactor to use a GHashTable with string hash
98
alarm_applet_sounds_load (AlarmApplet *applet)
101
AlarmListEntry *entry;
105
//g_assert (applet->alarms);
108
if (applet->sounds != NULL)
109
alarm_list_entry_list_free (&(applet->sounds));
112
applet->sounds = alarm_list_entry_list_new ("file://" ALARM_SOUNDSDIR,
113
supported_sound_mime_types);
115
// Load custom sounds from alarms
116
for (l = applet->alarms; l != NULL; l = l->next) {
117
alarm = ALARM (l->data);
119
for (l2 = applet->sounds; l2 != NULL; l2 = l2->next) {
120
entry = (AlarmListEntry *)l2->data;
121
if (strcmp (alarm->sound_file, entry->data) == 0) {
130
entry = alarm_list_entry_new_file (alarm->sound_file, NULL, NULL);
132
applet->sounds = g_list_append (applet->sounds, entry);
138
// Notify callback for changes to an alarm's sound_file
140
alarm_sound_file_changed (GObject *object,
144
Alarm *alarm = ALARM (object);
145
AlarmApplet *applet = (AlarmApplet *)data;
147
g_debug ("alarm_sound_file_changed: #%d", alarm->id);
149
// Reload sounds list
150
alarm_applet_sounds_load (applet);
164
gnome_da_xml_get_string (const xmlNode *parent, const gchar *val_name)
166
const gchar * const *sys_langs;
169
gchar *ret_val = NULL;
170
xmlChar *xml_val_name;
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);
178
#if GLIB_CHECK_VERSION (2, 6, 0)
179
sys_langs = g_get_language_names ();
182
xml_val_name = xmlCharStrdup (val_name);
183
len = xmlStrlen (xml_val_name);
185
for (element = parent->children; element != NULL; element = element->next) {
186
if (!xmlStrncmp (element->name, xml_val_name, len)) {
187
node_lang = xmlNodeGetLang (element);
189
if (node_lang == NULL) {
190
ret_val = (gchar *) xmlNodeGetContent (element);
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
206
xmlFree (xml_val_name);
211
get_app_command (const gchar *app)
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);
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");
222
g_hash_table_insert (app_command_map,
223
"banshee", "banshee --play");
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");
229
// Muine crashes and doesn't seem to have any play command
230
/*g_hash_table_insert (app_command_map,
234
return g_hash_table_lookup (app_command_map, app);
237
// Load stock apps into list
239
alarm_applet_apps_load (AlarmApplet *applet)
241
AlarmListEntry *entry;
242
gchar *filename, *name, *icon, *command;
244
xmlNode *root, *section, *element;
248
if (applet->apps != NULL)
249
alarm_list_entry_list_free (&(applet->apps));
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",
256
"gnome-default-applications.xml",
259
if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
260
g_critical ("Could not find %s.", filename);
264
xml_doc = xmlParseFile (filename);
267
g_warning ("Could not load %s.", filename);
271
root = xmlDocGetRootElement (xml_doc);
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");
282
// Lookup executable in app command map
283
tmp = get_app_command (executable);
285
command = g_strdup (tmp);
287
// Fall back to command specified in XML
288
command = gnome_da_xml_get_string (element, "command");
293
g_debug ("LOAD-APPS: Adding '%s': %s [%s]", name, command, icon);
295
entry = alarm_list_entry_new (name, command, icon);
301
applet->apps = g_list_append (applet->apps, entry);
316
// entry = alarm_list_entry_new("Rhythmbox Music Player", "rhythmbox", "rhythmbox");
317
// applet->apps = g_list_append (applet->apps, entry);
326
* Find the soonest-to-trigger upcoming alarm
329
alarm_applet_upcoming_alarm_update (AlarmApplet *applet)
334
applet->upcoming_alarm = NULL;
336
/* Loop through alarms looking for all active upcoming alarms and locate
337
* the one which will trigger sooner.
340
for (l = applet->alarms; l; l = l->next) {
343
if (!a->active) continue;
345
if (!applet->upcoming_alarm || a->timestamp < applet->upcoming_alarm->timestamp) {
346
// This alarm is most recent
347
applet->upcoming_alarm = a;
353
* Callback for when an alarm is activated / deactivated.
354
* We use this to update our closest upcoming alarm.
357
alarm_active_changed (GObject *object,
361
Alarm *alarm = ALARM (object);
362
AlarmApplet *applet = (AlarmApplet *)data;
364
g_debug ("alarm_active_changed: #%d to %d", alarm->id, alarm->active);
366
// Check if this was the upcoming alarm
367
if (!alarm->active) {
368
if (applet->upcoming_alarm == alarm) {
369
applet->upcoming_alarm = NULL;
371
alarm_applet_upcoming_alarm_update (applet);
372
alarm_applet_label_update (applet);
373
alarm_applet_icon_update (applet);
379
if (!applet->upcoming_alarm || alarm->timestamp < applet->upcoming_alarm->timestamp) {
381
applet->upcoming_alarm = alarm;
383
alarm_applet_label_update (applet);
390
* Callback for when an alarm is triggered
391
* We show the notification bubble here if appropriate.
394
alarm_triggered (Alarm *alarm, gpointer data)
396
AlarmApplet *applet = (AlarmApplet *)data;
398
g_debug ("Alarm triggered: #%d", alarm->id);
400
if (alarm->notify_bubble) {
401
g_debug ("Alarm #%d NOTIFICATION DISPLAY", alarm->id);
402
alarm_applet_notification_display (applet, alarm);
414
// TODO: Refactor to use a GHashTable instead
416
alarm_applet_alarms_load (AlarmApplet *applet)
418
if (applet->alarms != NULL) {
421
/* Free old alarm objects */
422
for (l = applet->alarms; l != NULL; l = l->next) {
423
g_object_unref (ALARM (l->data));
427
g_list_free (applet->alarms);
430
/* Fetch list of alarms */
431
applet->alarms = alarm_get_list (ALARM_GCONF_DIR);
435
alarm_applet_alarms_add (AlarmApplet *applet, Alarm *alarm)
437
applet->alarms = g_list_append (applet->alarms, alarm);
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);
443
g_signal_connect (alarm, "alarm", G_CALLBACK (alarm_triggered), applet);
447
alarm_applet_alarms_remove (AlarmApplet *applet, Alarm *alarm)
452
applet->alarms = g_list_remove (applet->alarms, alarm);
455
* Clear list store. This will decrease the refcount of our alarms by 1.
457
if (applet->list_alarms_store)
458
gtk_list_store_clear (applet->list_alarms_store);
460
g_debug ("alarm_applet_alarms_remove (..., %p): refcount = %d", alarm, G_OBJECT (alarm)->ref_count);
463
* If this is the upcoming alarm, update it.
465
if (applet->upcoming_alarm == alarm) {
466
alarm_applet_upcoming_alarm_update (applet);
470
* Remove any signal handlers for this alarm instance.
472
g_signal_handlers_disconnect_matched (alarm, 0, 0, 0, NULL, NULL, NULL);
477
g_object_unref (alarm);
485
alarm_applet_destroy (AlarmApplet *applet)
489
AlarmSettingsDialog *dialog;
491
g_debug ("AlarmApplet DESTROY");
494
if (applet->timer_id) {
495
g_source_remove (applet->timer_id);
498
// Destroy alarms list
499
if (applet->list_alarms_dialog) {
500
list_alarms_dialog_close (applet);
503
// Destroy preferences dialog
504
if (applet->preferences_dialog) {
505
gtk_widget_destroy (GTK_WIDGET (applet->preferences_dialog));
508
// Loop through all alarms and free like a mad man!
509
for (l = applet->alarms; l; l = l->next) {
512
// Check if a dialog is open for this alarm
513
dialog = (AlarmSettingsDialog *)g_hash_table_lookup (applet->edit_alarm_dialogs, (gconstpointer)a->id);
516
alarm_settings_dialog_close (dialog);
523
// Remove sounds list
524
if (applet->sounds) {
525
alarm_list_entry_list_free(&(applet->sounds));
530
alarm_list_entry_list_free(&(applet->apps));
533
if (app_command_map) {
534
g_hash_table_destroy (app_command_map);
535
app_command_map = NULL;
539
g_free (applet->gconf_dir);
541
// Finally free the AlarmApplet struct itself
551
alarm_applet_factory (PanelApplet *panelapplet,
557
if (strcmp (iid, "OAFIID:AlarmClock") != 0)
560
/* Initialize applet struct,
561
* fill with zero's */
562
applet = g_new0 (AlarmApplet, 1);
564
applet->parent = panelapplet;
565
applet->edit_alarm_dialogs = g_hash_table_new (NULL, NULL);
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;
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);
578
/* Set up gconf handlers */
579
alarm_applet_gconf_init (applet);
581
/* Load gconf values */
582
alarm_applet_gconf_load (applet);
585
alarm_applet_alarms_load (applet);
587
/* Find any upcoming active alarms */
588
alarm_applet_upcoming_alarm_update (applet);
590
/* Load sounds from alarms */
591
alarm_applet_sounds_load (applet);
593
/* Load apps for alarms */
594
alarm_applet_apps_load (applet);
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);
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);
606
/* Connect alarm trigger notify to all alarms */
607
alarm_signal_connect_list (applet->alarms, "alarm",
608
G_CALLBACK (alarm_triggered), applet);
610
/* Set up properties menu */
611
alarm_applet_menu_init (applet);
614
alarm_applet_ui_init (applet);
619
PANEL_APPLET_BONOBO_FACTORY ("OAFIID:AlarmClock_Factory",
623
alarm_applet_factory,