1 /*
2 * alarm.c -- Core alarm functionality
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 <stdio.h>
25 #include <time.h>
26 #include <string.h>
27
28 #include "alarm.h"
29
30 G_DEFINE_TYPE (Alarm, alarm, G_TYPE_OBJECT);
31
32 /* Prototypes and constants for property manipulation */
33 static void alarm_set_property(GObject *object,
34 guint prop_id,
35 const GValue *value,
36 GParamSpec *pspec);
37
38 static void alarm_get_property(GObject *object,
39 guint prop_id,
40 GValue *value,
41 GParamSpec *pspec);
42
43 static void alarm_constructed (GObject *object);
44
45 static void alarm_dispose (GObject *object);
46
47 static void alarm_gconf_associate_schemas (Alarm *alarm);
48
49 static void alarm_gconf_connect (Alarm *alarm);
50
51 static void alarm_gconf_disconnect (Alarm *alarm);
52
53 static void alarm_gconf_dir_changed (GConfClient *client,
54 guint cnxn_id,
55 GConfEntry *entry,
56 gpointer data);
57
58 static void alarm_timer_start (Alarm *alarm);
59 static void alarm_timer_remove (Alarm *alarm);
60 static gboolean alarm_timer_is_started (Alarm *alarm);
61
62 static void alarm_player_start (Alarm *alarm);
63 static void alarm_player_stop (Alarm *alarm);
64 static void alarm_command_run (Alarm *alarm);
65
66 #define ALARM_PRIVATE(o) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ALARM, AlarmPrivate))
68
69 typedef struct _AlarmPrivate AlarmPrivate;
70
71 struct _AlarmPrivate
72 {
73 GConfClient *gconf_client;
74 guint gconf_listener;
75 guint timer_id;
76 MediaPlayer *player;
77 guint player_timer_id;
78 };
79
80
81 static GConfEnumStringPair alarm_type_enum_map [] = {
82 { ALARM_TYPE_CLOCK, "clock" },
83 { ALARM_TYPE_TIMER, "timer" },
84 { 0, NULL }
85 };
86
87 static GConfEnumStringPair alarm_repeat_enum_map [] = {
88 { ALARM_REPEAT_SUN, "sun" },
89 { ALARM_REPEAT_MON, "mon" },
90 { ALARM_REPEAT_TUE, "tue" },
91 { ALARM_REPEAT_WED, "wed" },
92 { ALARM_REPEAT_THU, "thu" },
93 { ALARM_REPEAT_FRI, "fri" },
94 { ALARM_REPEAT_SAT, "sat" },
95 { 0, NULL }
96 };
97
98 static GConfEnumStringPair alarm_notify_type_enum_map [] = {
99 { ALARM_NOTIFY_SOUND, "sound" },
100 { ALARM_NOTIFY_COMMAND, "command" },
101 { 0, NULL }
102 };
103
104 /* Property indexes */
105 enum {
106 PROP_ALARM_0,
107 PROP_DIR,
108 PROP_ID,
109 PROP_TRIGGERED,
110 PROP_TYPE,
111 PROP_TIME,
112 PROP_TIMESTAMP,
113 PROP_ACTIVE,
114 PROP_MESSAGE,
115 PROP_REPEAT,
116 PROP_NOTIFY_TYPE,
117 PROP_SOUND_FILE,
118 PROP_SOUND_LOOP,
119 PROP_COMMAND
120 };
121
122 #define PROP_NAME_DIR "gconf-dir"
123 #define PROP_NAME_ID "id"
124 #define PROP_NAME_TRIGGERED "triggered"
125 #define PROP_NAME_TYPE "type"
126 #define PROP_NAME_TIME "time"
127 #define PROP_NAME_TIMESTAMP "timestamp"
128 #define PROP_NAME_ACTIVE "active"
129 #define PROP_NAME_MESSAGE "message"
130 #define PROP_NAME_REPEAT "repeat"
131 #define PROP_NAME_NOTIFY_TYPE "notify-type"
132 #define PROP_NAME_SOUND_FILE "sound-file"
133 #define PROP_NAME_SOUND_LOOP "sound-repeat"
134 #define PROP_NAME_COMMAND "command"
135
136 /* Signal indexes */
137 enum
138 {
139 SIGNAL_ALARM,
140 SIGNAL_CLEARED,
141 SIGNAL_ERROR,
142 SIGNAL_PLAYER,
143 LAST_SIGNAL
144 };
145
146 /* Signal identifier map */
147 static guint alarm_signal[LAST_SIGNAL] = {0, 0, 0, 0};
148
149 /* Prototypes for signal handlers */
150 static void alarm_alarm (Alarm *alarm);
151 static void alarm_cleared (Alarm *alarm);
152 static void alarm_error (Alarm *alarm, GError *err);
153 static void alarm_player_changed (Alarm *alarm, MediaPlayerState state);
154
155
156 /* Initialize the Alarm class */
157 static void
alarm_class_init(AlarmClass * class)158 alarm_class_init (AlarmClass *class)
159 {
160 GParamSpec *dir_param;
161 GParamSpec *id_param;
162 GParamSpec *triggered_param;
163 GParamSpec *type_param;
164 GParamSpec *time_param;
165 GParamSpec *timestamp_param;
166 GParamSpec *active_param;
167 GParamSpec *message_param;
168 GParamSpec *repeat_param;
169 GParamSpec *notify_type_param;
170 GParamSpec *sound_file_param;
171 GParamSpec *sound_loop_param;
172 GParamSpec *command_param;
173
174 GObjectClass *g_object_class;
175
176 /* get handle to base object */
177 g_object_class = G_OBJECT_CLASS (class);
178
179 /* << miscellaneous initialization >> */
180
181 /* create GParamSpec descriptions for properties */
182 dir_param = g_param_spec_string (PROP_NAME_DIR,
183 "GConf dir",
184 "GConf base directory",
185 NULL,
186 G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
187
188 id_param = g_param_spec_uint (PROP_NAME_ID,
189 "alarm id",
190 "id of the alarm",
191 0, /* min */
192 UINT_MAX, /* max */
193 0, /* default */
194 G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
195
196 triggered_param = g_param_spec_boolean (PROP_NAME_TRIGGERED,
197 "alarm triggered",
198 "triggered flag of the alarm",
199 FALSE,
200 G_PARAM_READABLE);
201
202 type_param = g_param_spec_uint (PROP_NAME_TYPE,
203 "alarm type",
204 "type of the alarm",
205 ALARM_TYPE_CLOCK,
206 ALARM_TYPE_TIMER,
207 ALARM_DEFAULT_TYPE,
208 G_PARAM_READWRITE);
209
210
211 time_param = g_param_spec_uint (PROP_NAME_TIME,
212 "alarm time",
213 "time when the alarm should trigger",
214 0, /* min */
215 UINT_MAX, /* max */
216 ALARM_DEFAULT_TIME, /* default */
217 G_PARAM_READWRITE);
218
219 timestamp_param = g_param_spec_uint (PROP_NAME_TIMESTAMP,
220 "alarm timestamp",
221 "UNIX timestamp when the alarm should trigger",
222 0, /* min */
223 UINT_MAX, /* max */
224 ALARM_DEFAULT_TIME, /* default */
225 G_PARAM_READWRITE);
226
227 active_param = g_param_spec_boolean (PROP_NAME_ACTIVE,
228 "active alarm",
229 "whether the alarm is on or not",
230 ALARM_DEFAULT_ACTIVE,
231 G_PARAM_READWRITE);
232
233 message_param = g_param_spec_string (PROP_NAME_MESSAGE,
234 "alarm message",
235 "message which describes this alarm",
236 ALARM_DEFAULT_MESSAGE,
237 G_PARAM_READWRITE);
238
239 repeat_param = g_param_spec_uint (PROP_NAME_REPEAT,
240 "repeat",
241 "repeat the alarm",
242 ALARM_REPEAT_NONE, /* min */
243 ALARM_REPEAT_ALL, /* max */
244 ALARM_DEFAULT_REPEAT, /* default */
245 G_PARAM_READWRITE);
246
247 notify_type_param = g_param_spec_uint (PROP_NAME_NOTIFY_TYPE,
248 "notification type",
249 "what kind of notification should be used",
250 ALARM_NOTIFY_SOUND,
251 ALARM_NOTIFY_COMMAND,
252 ALARM_DEFAULT_NOTIFY_TYPE,
253 G_PARAM_READWRITE);
254
255 sound_file_param = g_param_spec_string (PROP_NAME_SOUND_FILE,
256 "sound file",
257 "sound file to play",
258 ALARM_DEFAULT_SOUND_FILE,
259 G_PARAM_READWRITE);
260
261 sound_loop_param = g_param_spec_boolean (PROP_NAME_SOUND_LOOP,
262 "sound loop",
263 "whether the sound should be looped",
264 ALARM_DEFAULT_SOUND_LOOP,
265 G_PARAM_READWRITE);
266
267 command_param = g_param_spec_string (PROP_NAME_COMMAND,
268 "command",
269 "command to run",
270 ALARM_DEFAULT_COMMAND,
271 G_PARAM_READWRITE);
272
273 /* override base object methods */
274 g_object_class->set_property = alarm_set_property;
275 g_object_class->get_property = alarm_get_property;
276 g_object_class->constructed = alarm_constructed;
277 g_object_class->dispose = alarm_dispose;
278
279 /* install properties */
280 g_object_class_install_property (g_object_class, PROP_DIR, dir_param);
281 g_object_class_install_property (g_object_class, PROP_ID, id_param);
282 g_object_class_install_property (g_object_class, PROP_TRIGGERED, triggered_param);
283 g_object_class_install_property (g_object_class, PROP_TYPE, type_param);
284 g_object_class_install_property (g_object_class, PROP_TIME, time_param);
285 g_object_class_install_property (g_object_class, PROP_TIMESTAMP, timestamp_param);
286 g_object_class_install_property (g_object_class, PROP_ACTIVE, active_param);
287 g_object_class_install_property (g_object_class, PROP_MESSAGE, message_param);
288 g_object_class_install_property (g_object_class, PROP_REPEAT, repeat_param);
289 g_object_class_install_property (g_object_class, PROP_NOTIFY_TYPE, notify_type_param);
290 g_object_class_install_property (g_object_class, PROP_SOUND_FILE, sound_file_param);
291 g_object_class_install_property (g_object_class, PROP_SOUND_LOOP, sound_loop_param);
292 g_object_class_install_property (g_object_class, PROP_COMMAND, command_param);
293
294 g_type_class_add_private (class, sizeof (AlarmPrivate));
295
296 /* set signal handlers */
297 class->alarm = alarm_alarm;
298 class->cleared = alarm_cleared;
299 class->error = alarm_error;
300 class->player_changed = alarm_player_changed;
301
302 /* install signals and default handlers */
303 alarm_signal[SIGNAL_ALARM] = g_signal_new ("alarm", /* name */
304 TYPE_ALARM, /* class type identifier */
305 G_SIGNAL_RUN_FIRST, /* options */
306 G_STRUCT_OFFSET (AlarmClass, alarm), /* handler offset */
307 NULL, /* accumulator function */
308 NULL, /* accumulator data */
309 g_cclosure_marshal_VOID__VOID, /* marshaller */
310 G_TYPE_NONE, /* type of return value */
311 0);
312
313 alarm_signal[SIGNAL_CLEARED] = g_signal_new ("cleared",
314 TYPE_ALARM,
315 G_SIGNAL_RUN_FIRST,
316 G_STRUCT_OFFSET (AlarmClass, cleared),
317 NULL,
318 NULL,
319 g_cclosure_marshal_VOID__VOID,
320 G_TYPE_NONE,
321 0);
322
323 alarm_signal[SIGNAL_ERROR] = g_signal_new ("error",
324 TYPE_ALARM,
325 G_SIGNAL_RUN_LAST,
326 G_STRUCT_OFFSET (AlarmClass, error),
327 NULL,
328 NULL,
329 g_cclosure_marshal_VOID__POINTER,
330 G_TYPE_NONE,
331 1,
332 G_TYPE_POINTER);
333
334 alarm_signal[SIGNAL_PLAYER] = g_signal_new ("player_changed", /* name */
335 TYPE_ALARM, /* class type identifier */
336 G_SIGNAL_RUN_LAST, /* options */
337 G_STRUCT_OFFSET (AlarmClass, player_changed), /* handler offset */
338 NULL, /* accumulator function */
339 NULL, /* accumulator data */
340 g_cclosure_marshal_VOID__UINT, /* marshaller */
341 G_TYPE_NONE, /* type of return value */
342 1,
343 G_TYPE_UINT);
344 }
345
346 /*
347 * Utility function for extracting the strings out of a GConfValue of type
348 * GCONF_VALUE_LIST and placing them in a plan GSList of strings.
349 *
350 * Note: You should free the GSList returned but NOT the string contents.
351 */
352 static GSList *
alarm_gconf_extract_list_string(GConfValue * val)353 alarm_gconf_extract_list_string (GConfValue *val)
354 {
355 GSList *list, *new_list, *l;
356
357 g_assert (GCONF_VALUE_STRING == gconf_value_get_list_type (val));
358
359 /* Fetch GSList of GConfValues. Extract them and put into a plain list of strings.
360 * Note that the returned string from gconf_value_get_string() is owned by the GConfValue
361 * and should thus NOT be freed.
362 */
363 list = gconf_value_get_list (val);
364 new_list = NULL;
365 for (l = list; l; l = l->next) {
366 new_list = g_slist_append (new_list, (gpointer) gconf_value_get_string ((GConfValue *)l->data));
367 }
368
369 return new_list;
370 }
371
372 static void
alarm_gconf_load(Alarm * alarm)373 alarm_gconf_load (Alarm *alarm)
374 {
375 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
376 GConfClient *client = priv->gconf_client;
377 GConfValue *val;
378 gchar *key, *tmp;
379 GSList *list;
380 guint i;
381
382 /*
383 * TYPE
384 */
385 key = alarm_gconf_get_full_key (alarm, PROP_NAME_TYPE);
386 tmp = gconf_client_get_string (client, key, NULL);
387 g_free (key);
388
389 i = alarm_type_from_string (tmp);
390
391 if (i > 0) {
392 alarm->type = i;
393 } else {
394 // Not found in GConf, fall back to defaults
395 alarm->type = ALARM_DEFAULT_TYPE;
396 g_object_set (alarm, PROP_NAME_TYPE, ALARM_DEFAULT_TYPE, NULL);
397 }
398
399 if (tmp)
400 g_free (tmp);
401
402 /*
403 * TIME
404 */
405 key = alarm_gconf_get_full_key (alarm, PROP_NAME_TIME);
406 val = gconf_client_get (client, key, NULL);
407 g_free (key);
408
409 if (val) {
410 alarm->time = (time_t)gconf_value_get_int (val);
411 gconf_value_free (val);
412 } else {
413 // Not found in GConf, fall back to defaults
414 g_object_set (alarm, PROP_NAME_TIME, ALARM_DEFAULT_TIME, NULL);
415 }
416
417 /*
418 * TIMESTAMP
419 */
420 key = alarm_gconf_get_full_key (alarm, PROP_NAME_TIMESTAMP);
421 val = gconf_client_get (client, key, NULL);
422 g_free (key);
423
424 if (val) {
425 alarm->timestamp = (time_t)gconf_value_get_int (val);
426 gconf_value_free (val);
427 } else {
428 // Not found in GConf, fall back to defaults
429 g_object_set (alarm, PROP_NAME_TIMESTAMP, ALARM_DEFAULT_TIMESTAMP, NULL);
430 }
431
432 /*
433 * ACTIVE
434 */
435 key = alarm_gconf_get_full_key (alarm, PROP_NAME_ACTIVE);
436 val = gconf_client_get (client, key, NULL);
437 g_free (key);
438
439 if (val) {
440 // We g_object_set here so the timer will be started for
441 // active alarms
442 g_object_set (alarm, "active", gconf_value_get_bool (val), NULL);
443 gconf_value_free (val);
444 } else {
445 // Not found in GConf, fall back to defaults
446 g_object_set (alarm, PROP_NAME_ACTIVE, ALARM_DEFAULT_ACTIVE, NULL);
447 }
448
449 /*
450 * MESSAGE
451 */
452 key = alarm_gconf_get_full_key (alarm, PROP_NAME_MESSAGE);
453 tmp = gconf_client_get_string (client, key, NULL);
454 g_free (key);
455
456 if (tmp) {
457 alarm->message = tmp;
458 } else {
459 // Not found in GConf, fall back to defaults
460 g_object_set (alarm, PROP_NAME_MESSAGE, ALARM_DEFAULT_MESSAGE, NULL);
461 }
462
463 /*
464 * REPEAT
465 */
466 key = alarm_gconf_get_full_key (alarm, PROP_NAME_REPEAT);
467 val = gconf_client_get (client, key, NULL);
468 g_free (key);
469
470 if (val) {
471 list = alarm_gconf_extract_list_string (val);
472
473 alarm->repeat = alarm_repeat_from_list (list);
474
475 g_slist_free (list);
476 gconf_value_free (val);
477 } else {
478 // Not found in GConf, fall back to defaults
479 g_object_set (alarm, PROP_NAME_REPEAT, ALARM_DEFAULT_REPEAT, NULL);
480 }
481
482 /*
483 * NOTIFY TYPE
484 */
485 key = alarm_gconf_get_full_key (alarm, PROP_NAME_NOTIFY_TYPE);
486 tmp = gconf_client_get_string (client, key, NULL);
487 g_free (key);
488
489 i = alarm_notify_type_from_string (tmp);
490
491 if (i > 0) {
492 alarm->notify_type = i;
493 } else {
494 // Not found in GConf, fall back to defaults
495 g_object_set (alarm, PROP_NAME_NOTIFY_TYPE, ALARM_DEFAULT_NOTIFY_TYPE, NULL);
496 }
497
498 if (tmp)
499 g_free (tmp);
500
501 /*
502 * SOUND FILE
503 */
504 key = alarm_gconf_get_full_key (alarm, PROP_NAME_SOUND_FILE);
505 tmp = gconf_client_get_string (client, key, NULL);
506 g_free (key);
507
508 if (tmp) {
509 alarm->sound_file = tmp;
510 } else {
511 // Not found in GConf, fall back to defaults
512 g_object_set (alarm, PROP_NAME_SOUND_FILE, ALARM_DEFAULT_SOUND_FILE, NULL);
513 }
514
515 /*
516 * SOUND LOOP
517 */
518 key = alarm_gconf_get_full_key (alarm, PROP_NAME_SOUND_LOOP);
519 val = gconf_client_get (client, key, NULL);
520 g_free (key);
521
522 if (val) {
523 alarm->sound_loop = gconf_value_get_bool (val);
524 gconf_value_free (val);
525 } else {
526 // Not found in GConf, fall back to defaults
527 g_object_set (alarm, PROP_NAME_SOUND_LOOP, ALARM_DEFAULT_SOUND_LOOP, NULL);
528 }
529
530
531 /*
532 * COMMAND
533 */
534 key = alarm_gconf_get_full_key (alarm, PROP_NAME_COMMAND);
535 tmp = gconf_client_get_string (client, key, NULL);
536 g_free (key);
537
538 if (tmp) {
539 alarm->command = tmp;
540 } else {
541 // Not found in GConf, fall back to defaults
542 g_object_set (alarm, PROP_NAME_COMMAND, ALARM_DEFAULT_COMMAND, NULL);
543 }
544 }
545
546 static void
alarm_init(Alarm * self)547 alarm_init (Alarm *self)
548 {
549 AlarmPrivate *priv = ALARM_PRIVATE (self);
550
551 self->gconf_dir = NULL;
552 self->id = -1;
553
554 priv->gconf_listener = 0;
555 priv->gconf_client = gconf_client_get_default ();
556 }
557
558 static void
alarm_constructed(GObject * object)559 alarm_constructed (GObject *object)
560 {
561 Alarm *alarm = ALARM (object);
562
563 // Load gconf settings
564 alarm_gconf_load (alarm);
565
566 // Connect gconf listener
567 //alarm_gconf_connect (alarm);
568 }
569
570 /* set an Alarm property */
571 static void
alarm_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)572 alarm_set_property (GObject *object,
573 guint prop_id,
574 const GValue *value,
575 GParamSpec *pspec)
576 {
577 Alarm *alarm;
578 AlarmPrivate *priv = ALARM_PRIVATE (object);
579
580 GConfClient *client;
581 GError *err = NULL;
582
583 const gchar *str;
584 guint d;
585 gboolean b;
586 GSList *list;
587
588 gchar *key, *tmp;
589
590 alarm = ALARM (object);
591 client = priv->gconf_client;
592
593 // DEBUGGING INFO
594 GValue strval = {0};
595 g_value_init (&strval, G_TYPE_STRING);
596 g_value_transform (value, &strval);
597 g_debug ("Alarm(%p) #%d: set %s=%s", alarm, alarm->id, pspec->name,
598 g_value_get_string(&strval));
599
600 switch (prop_id) {
601 case PROP_DIR:
602 str = g_value_get_string (value);
603
604 /* Validate */
605 if (!str) {
606 g_critical ("Invalid gconf-dir value: \"%s\": NULL", str);
607 return;
608 }
609
610 if (!gconf_valid_key (str, &tmp)) {
611 g_critical ("Invalid gconf-dir value: \"%s\": %s", str, tmp);
612 g_free (tmp);
613 return;
614 }
615
616 if (!alarm->gconf_dir || strcmp (str, alarm->gconf_dir) != 0) {
617 // Changed, remove old gconf listeners
618 alarm_gconf_disconnect (alarm);
619
620 if (alarm->gconf_dir != NULL)
621 g_free (alarm->gconf_dir);
622 alarm->gconf_dir = g_strdup (str);
623
624 // Associate schemas
625 alarm_gconf_associate_schemas (alarm);
626
627 alarm_gconf_connect (alarm);
628 }
629 break;
630 case PROP_ID:
631 d = g_value_get_uint (value);
632
633 if (d != alarm->id) {
634 alarm_gconf_disconnect (alarm);
635 alarm->id = d;
636
637 alarm_gconf_associate_schemas (alarm);
638
639 alarm_gconf_load (alarm);
640 alarm_gconf_connect (alarm);
641 }
642 break;
643 case PROP_TRIGGERED:
644 // TODO: Should we map this to a GConf value?
645 // The only case where this might be useful is where the program
646 // is restarted while having a triggered alarm so that we could
647 // re-trigger it when the program starts again...
648 b = g_value_get_boolean (value);
649
650 if (b != alarm->triggered) {
651 alarm->triggered = b;
652 }
653
654 case PROP_TYPE:
655 alarm->type = g_value_get_uint (value);
656
657 if (alarm->active) {
658 // Update timestamp
659 alarm_update_timestamp (alarm);
660 }
661
662 key = alarm_gconf_get_full_key (alarm, PROP_NAME_TYPE);
663
664 if (!gconf_client_set_string (client, key,
665 alarm_type_to_string (alarm->type),
666 &err)) {
667
668 g_critical ("Could not set %s (gconf): %s",
669 key, err->message);
670
671 g_error_free (err);
672 }
673
674 g_free (key);
675 break;
676 case PROP_TIME:
677 alarm->time = g_value_get_uint (value);
678
679 if (alarm->active) {
680 // Update timestamp
681 alarm_update_timestamp (alarm);
682 }
683
684 key = alarm_gconf_get_full_key (alarm, PROP_NAME_TIME);
685
686 if (!gconf_client_set_int (client, key, alarm->time, &err)) {
687
688 g_critical ("Could not set %s (gconf): %s",
689 key, err->message);
690
691 g_error_free (err);
692 }
693
694 g_free (key);
695 break;
696 case PROP_TIMESTAMP:
697 alarm->timestamp = g_value_get_uint (value);
698
699 key = alarm_gconf_get_full_key (alarm, PROP_NAME_TIMESTAMP);
700
701 if (!gconf_client_set_int (client, key, alarm->timestamp, &err)) {
702
703 g_critical ("Could not set %s (gconf): %s",
704 key, err->message);
705
706 g_error_free (err);
707 }
708
709 g_free (key);
710 break;
711 case PROP_ACTIVE:
712 b = alarm->active;
713 alarm->active = g_value_get_boolean (value);
714
715 //g_debug ("[%p] #%d ACTIVE: old=%d new=%d", alarm, alarm->id, b, alarm->active);
716 if (alarm->active && !alarm_timer_is_started(alarm)) {
717 // Start timer
718 alarm_timer_start (alarm);
719 }
720 else if (!alarm->active && alarm_timer_is_started(alarm)) {
721 // Stop timer
722 alarm_timer_remove (alarm);
723 }
724
725
726 key = alarm_gconf_get_full_key (alarm, PROP_NAME_ACTIVE);
727
728 if (!gconf_client_set_bool (client, key, alarm->active, &err)) {
729
730 g_critical ("Could not set %s (gconf): %s",
731 key, err->message);
732
733 g_error_free (err);
734 }
735
736 g_free (key);
737 break;
738 case PROP_MESSAGE:
739 if (alarm->message)
740 g_free (alarm->message);
741
742 alarm->message = g_strdup (g_value_get_string (value));
743
744 key = alarm_gconf_get_full_key (alarm, PROP_NAME_MESSAGE);
745
746 if (!gconf_client_set_string (client, key,
747 alarm->message,
748 &err)) {
749
750 g_critical ("Could not set %s (gconf): %s",
751 key, err->message);
752
753 g_error_free (err);
754 }
755
756 g_free (key);
757
758 break;
759 case PROP_REPEAT:
760 alarm->repeat = g_value_get_uint (value);
761
762 if (alarm->active) {
763 alarm_update_timestamp (alarm);
764 }
765
766 key = alarm_gconf_get_full_key (alarm, PROP_NAME_REPEAT);
767 list = alarm_repeat_to_list (alarm->repeat);
768
769 if (!gconf_client_set_list(client, key,
770 GCONF_VALUE_STRING, list,
771 &err)) {
772
773 g_critical ("Could not set %s (gconf): %s",
774 key, err->message);
775
776 g_error_free (err);
777 }
778
779 g_slist_free (list);
780 g_free (key);
781
782 break;
783 case PROP_NOTIFY_TYPE:
784 alarm->notify_type = g_value_get_uint (value);
785
786 key = alarm_gconf_get_full_key (alarm, PROP_NAME_NOTIFY_TYPE);
787
788 if (!gconf_client_set_string (client, key,
789 alarm_notify_type_to_string (alarm->notify_type),
790 &err)) {
791
792 g_critical ("Could not set %s (gconf): %s",
793 key, err->message);
794
795 g_error_free (err);
796 }
797
798 g_free (key);
799
800 break;
801 case PROP_SOUND_FILE:
802 alarm->sound_file = g_strdup (g_value_get_string (value));
803
804 key = alarm_gconf_get_full_key (alarm, PROP_NAME_SOUND_FILE);
805
806 if (!gconf_client_set_string (client, key,
807 alarm->sound_file,
808 &err)) {
809
810 g_critical ("Could not set %s (gconf): %s",
811 key, err->message);
812
813 g_error_free (err);
814 }
815
816 g_free (key);
817
818 break;
819 case PROP_SOUND_LOOP:
820 alarm->sound_loop = g_value_get_boolean (value);
821
822 key = alarm_gconf_get_full_key (alarm, PROP_NAME_SOUND_LOOP);
823
824 if (!gconf_client_set_bool (client, key, alarm->sound_loop, &err)) {
825
826 g_critical ("Could not set %s (gconf): %s",
827 key, err->message);
828
829 g_error_free (err);
830 }
831
832 g_free (key);
833 break;
834 case PROP_COMMAND:
835 alarm->command = g_strdup (g_value_get_string (value));
836
837 key = alarm_gconf_get_full_key (alarm, PROP_NAME_COMMAND);
838
839 if (!gconf_client_set_string (client, key,
840 alarm->command,
841 &err)) {
842
843 g_critical ("Could not set %s (gconf): %s",
844 key, err->message);
845
846 g_error_free (err);
847 }
848
849 g_free (key);
850
851 break;
852 default:
853 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
854 break;
855 }
856 }
857
858 /* retrive an Alarm property */
859 static void
alarm_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)860 alarm_get_property (GObject *object,
861 guint prop_id,
862 GValue *value,
863 GParamSpec *pspec)
864 {
865 Alarm *alarm = ALARM (object);
866
867 switch (prop_id) {
868 case PROP_DIR:
869 g_value_set_string (value, alarm->gconf_dir);
870 break;
871 case PROP_ID:
872 g_value_set_uint (value, alarm->id);
873 break;
874 case PROP_TRIGGERED:
875 g_value_set_boolean (value, alarm->triggered);
876 break;
877 case PROP_TYPE:
878 g_value_set_uint (value, alarm->type);
879 break;
880 case PROP_TIME:
881 g_value_set_uint (value, alarm->time);
882 break;
883 case PROP_TIMESTAMP:
884 g_value_set_uint (value, alarm->timestamp);
885 break;
886 case PROP_ACTIVE:
887 g_value_set_boolean (value, alarm->active);
888 break;
889 case PROP_MESSAGE:
890 g_value_set_string (value, alarm->message);
891 break;
892 case PROP_REPEAT:
893 g_value_set_uint (value, alarm->repeat);
894 break;
895 case PROP_NOTIFY_TYPE:
896 g_value_set_uint (value, alarm->notify_type);
897 break;
898 case PROP_SOUND_FILE:
899 g_value_set_string (value, alarm->sound_file);
900 break;
901 case PROP_SOUND_LOOP:
902 g_value_set_boolean (value, alarm->sound_loop);
903 break;
904 case PROP_COMMAND:
905 g_value_set_string (value, alarm->command);
906 break;
907 default:
908 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
909 break;
910 }
911 }
912
913 /*
914 * ERROR signal {{
915 */
916 GQuark
alarm_error_quark(void)917 alarm_error_quark (void)
918 {
919 return g_quark_from_static_string ("alarm-error-quark");
920 }
921
922 static void
alarm_error(Alarm * alarm,GError * err)923 alarm_error (Alarm *alarm, GError *err)
924 {
925 g_critical ("Alarm(%p) #%d: alarm_error: #%d: %s", alarm, alarm->id, err->code, err->message);
926 }
927
928 void
alarm_error_trigger(Alarm * alarm,AlarmErrorCode code,const gchar * msg)929 alarm_error_trigger (Alarm *alarm, AlarmErrorCode code, const gchar *msg)
930 {
931 GError *err = g_error_new (ALARM_ERROR, code, "%s", msg);
932
933 g_signal_emit (alarm, alarm_signal[SIGNAL_ERROR], 0, err, NULL);
934
935 g_error_free (err);
936 err = NULL;
937 }
938
939 /*
940 * }} ERROR signal
941 */
942
943 static void
alarm_player_changed(Alarm * alarm,MediaPlayerState state)944 alarm_player_changed (Alarm *alarm, MediaPlayerState state)
945 {
946 g_debug ("Alarm(%p) #%d: player_changed to %d", alarm, alarm->id, state);
947 }
948
949
950 /*
951 * ALARM signal {{
952 */
953
954 static void
alarm_alarm(Alarm * alarm)955 alarm_alarm (Alarm *alarm)
956 {
957 g_debug ("Alarm(%p) #%d: alarm() DING!", alarm, alarm->id);
958
959 // Clear first, if needed
960 alarm_clear (alarm);
961
962 // Update triggered flag
963 alarm->triggered = TRUE;
964
965 // Do we want to repeat this alarm?
966 if (alarm_should_repeat (alarm)) {
967 g_debug ("Alarm(%p) #%d: alarm() Repeating...", alarm, alarm->id);
968 alarm_update_timestamp (alarm);
969 } else {
970 alarm_disable (alarm);
971 }
972
973 switch (alarm->notify_type) {
974 case ALARM_NOTIFY_SOUND:
975 // Start sound playback
976 g_debug("Alarm(%p) #%d: alarm() Start player", alarm, alarm->id);
977 alarm_player_start (alarm);
978 break;
979 case ALARM_NOTIFY_COMMAND:
980 // Start app
981 g_debug("Alarm(%p) #%d: alarm() Start command", alarm, alarm->id);
982 alarm_command_run (alarm);
983 break;
984 default:
985 g_warning ("Alarm(%p) #%d: UNKNOWN NOTIFICATION TYPE %d",
986 alarm, alarm->id, alarm->notify_type);
987 }
988 }
989
990 void
alarm_trigger(Alarm * alarm)991 alarm_trigger (Alarm *alarm)
992 {
993 g_signal_emit (alarm, alarm_signal[SIGNAL_ALARM], 0, NULL);
994 }
995
996 /*
997 * Convenience functions for enabling/disabling the alarm.
998 *
999 * Will update timestamp if needed.
1000 */
1001 void
alarm_set_enabled(Alarm * alarm,gboolean enabled)1002 alarm_set_enabled (Alarm *alarm, gboolean enabled)
1003 {
1004 if (enabled) {
1005 alarm_update_timestamp (alarm);
1006 }
1007
1008 g_object_set (alarm, "active", enabled, NULL);
1009 }
1010
1011 void
alarm_enable(Alarm * alarm)1012 alarm_enable (Alarm *alarm)
1013 {
1014 alarm_set_enabled (alarm, TRUE);
1015 }
1016
1017 void
alarm_disable(Alarm * alarm)1018 alarm_disable (Alarm *alarm)
1019 {
1020 alarm_set_enabled (alarm, FALSE);
1021 }
1022
1023 /*
1024 * Delete alarm. This will remove all configuration
1025 * associated with this alarm.
1026 */
1027 void
alarm_delete(Alarm * alarm)1028 alarm_delete (Alarm *alarm)
1029 {
1030 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1031 GConfClient *client = priv->gconf_client;
1032 gchar *key;
1033
1034 // Disconnect gconf listeners
1035 alarm_gconf_disconnect (alarm);
1036
1037 // Remove configuration
1038 key = alarm_gconf_get_dir (alarm);
1039 g_debug ("Alarm(%p) #%d: alarm_delete() recursive unset on %s", alarm, alarm->id, key);
1040 gconf_client_recursive_unset (client, key, GCONF_UNSET_INCLUDING_SCHEMA_NAMES, NULL);
1041 gconf_client_suggest_sync (client, NULL);
1042 g_free (key);
1043 }
1044
1045 /*
1046 * Snooze the alarm for a number of seconds.
1047 */
1048 void
alarm_snooze(Alarm * alarm,guint seconds)1049 alarm_snooze (Alarm *alarm, guint seconds)
1050 {
1051 g_assert (alarm->triggered);
1052
1053 g_debug ("Alarm(%p) #%d: snooze() for %d minutes", alarm, alarm->id, seconds / 60);
1054
1055 // Silence!
1056 alarm_clear (alarm);
1057
1058 // Remind later
1059 time_t now = time (NULL);
1060
1061 g_object_set (alarm,
1062 "timestamp", now + seconds,
1063 "active", TRUE,
1064 NULL);
1065
1066 // alarm_timer_start (alarm);
1067 }
1068
1069 /**
1070 * Alarm cleared signal
1071 */
1072 static void
alarm_cleared(Alarm * alarm)1073 alarm_cleared (Alarm *alarm)
1074 {
1075 g_debug ("Alarm(%p) #%d: cleared()", alarm, alarm->id);
1076
1077 // Update triggered flag
1078 alarm->triggered = FALSE;
1079
1080 // Stop player
1081 alarm_player_stop (alarm);
1082 }
1083
1084 /*
1085 * Clear the alarm. Resets the triggered flag and emits the "cleared" signal.
1086 * This will also stop any running players.
1087 */
1088 void
alarm_clear(Alarm * alarm)1089 alarm_clear (Alarm *alarm)
1090 {
1091 if (alarm->triggered) {
1092 g_signal_emit (alarm, alarm_signal[SIGNAL_CLEARED], 0, NULL);
1093 }
1094 }
1095
1096 /*
1097 * Is the alarm playing?
1098 */
1099 gboolean
alarm_is_playing(Alarm * a)1100 alarm_is_playing (Alarm *a)
1101 {
1102 AlarmPrivate *priv = ALARM_PRIVATE (a);
1103
1104 return priv->player && priv->player->state == MEDIA_PLAYER_PLAYING;
1105 }
1106
1107
1108
1109 static gboolean
alarm_timer_update(Alarm * alarm)1110 alarm_timer_update (Alarm *alarm)
1111 {
1112 time_t now;
1113
1114 time (&now);
1115
1116 if (now >= alarm->timestamp) {
1117 alarm_trigger (alarm);
1118
1119 // Remove callback only if we don't intend to repeat the alarm
1120 return alarm_should_repeat (alarm);
1121 } else if (alarm->timestamp - now <= 10) {
1122 g_debug ("Alarm(%p) #%d: -%2d...", alarm, alarm->id, (int)(alarm->timestamp - now));
1123 }
1124
1125 // Keep callback
1126 return TRUE;
1127 }
1128
1129 static void
alarm_timer_start(Alarm * alarm)1130 alarm_timer_start (Alarm *alarm)
1131 {
1132 AlarmPrivate *priv = ALARM_PRIVATE(alarm);
1133
1134 g_debug ("Alarm(%p) #%d: timer_start()", alarm, alarm->id);
1135
1136 // Remove old timer, if any
1137 alarm_timer_remove (alarm);
1138
1139 // Keep us updated every 1 second
1140 priv->timer_id = g_timeout_add_seconds (1, (GSourceFunc) alarm_timer_update, alarm);
1141 }
1142
1143 static gboolean
alarm_timer_is_started(Alarm * alarm)1144 alarm_timer_is_started (Alarm *alarm)
1145 {
1146 AlarmPrivate *priv = ALARM_PRIVATE(alarm);
1147
1148 return priv->timer_id > 0;
1149 }
1150
1151 static void
alarm_timer_remove(Alarm * alarm)1152 alarm_timer_remove (Alarm *alarm)
1153 {
1154 AlarmPrivate *priv = ALARM_PRIVATE(alarm);
1155
1156 if (alarm_timer_is_started(alarm)) {
1157 g_debug ("Alarm(%p) #%d: timer_remove", alarm, alarm->id);
1158
1159 g_source_remove (priv->timer_id);
1160
1161 priv->timer_id = 0;
1162 }
1163 }
1164
1165
1166 /*
1167 * }} ALARM signal
1168 */
1169
1170 /*
1171 * Taken from panel-applet.c
1172 */
1173 static void
alarm_gconf_associate_schemas(Alarm * alarm)1174 alarm_gconf_associate_schemas (Alarm *alarm)
1175 {
1176 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1177 GConfClient *client = priv->gconf_client;
1178 GSList *list, *l;
1179 GError *error = NULL;
1180
1181 if (alarm->id < 0)
1182 return;
1183
1184 list = gconf_client_all_entries (client, ALARM_GCONF_SCHEMA_DIR, &error);
1185
1186 g_return_if_fail (error == NULL);
1187
1188 for (l = list; l; l = l->next) {
1189 GConfEntry *entry = l->data;
1190 gchar *key;
1191 gchar *tmp;
1192
1193 tmp = g_path_get_basename (gconf_entry_get_key (entry));
1194
1195 if (strchr (tmp, '-'))
1196 g_warning ("Applet key '%s' contains a hyphen. Please "
1197 "use underscores in gconf keys\n", tmp);
1198
1199 key = alarm_gconf_get_full_key (alarm, tmp);
1200
1201 g_free (tmp);
1202
1203 gconf_engine_associate_schema (
1204 client->engine, key, gconf_entry_get_key (entry), &error);
1205
1206 g_free (key);
1207
1208 gconf_entry_free (entry);
1209
1210 if (error) {
1211 g_slist_free (list);
1212 return;
1213 }
1214 }
1215
1216 g_slist_free (list);
1217 }
1218
1219 static void
alarm_gconf_connect(Alarm * alarm)1220 alarm_gconf_connect (Alarm *alarm)
1221 {
1222 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1223 gchar *dir;
1224 GError *err = NULL;
1225
1226 // g_debug ("gconf_connect (%p) ? %d", alarm, IS_ALARM ((gpointer)alarm));
1227
1228 if (alarm->id < 0)
1229 return;
1230
1231 dir = alarm_gconf_get_dir (alarm);
1232
1233 // g_debug ("alarm_gconf_connect (%p) to dir %s", alarm, dir);
1234
1235 gconf_client_add_dir (priv->gconf_client, dir,
1236 GCONF_CLIENT_PRELOAD_ONELEVEL, &err);
1237
1238 if (err) {
1239 g_warning ("alarm_gconf_connect (%p): gconf_client_add_dir (%s) failed: %s", alarm, dir, err->message);
1240 g_error_free (err);
1241 err = NULL;
1242 }
1243
1244 priv->gconf_listener =
1245 gconf_client_notify_add (
1246 priv->gconf_client, dir,
1247 (GConfClientNotifyFunc) alarm_gconf_dir_changed,
1248 alarm, NULL, &err);
1249
1250 if (err) {
1251 g_warning ("alarm_gconf_connect (%p): gconf_client_notify_add (%s) failed: %s", alarm, dir, err->message);
1252 g_error_free (err);
1253 err = NULL;
1254 }
1255
1256 // g_debug ("alarm_gconf_connect: Added listener %d to alarm #%d %p", priv->gconf_listener, alarm->id, alarm);
1257
1258 g_free (dir);
1259 }
1260
1261 static void
alarm_gconf_disconnect(Alarm * alarm)1262 alarm_gconf_disconnect (Alarm *alarm)
1263 {
1264 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1265 gchar *dir;
1266
1267 if (priv->gconf_listener) {
1268 //g_debug ("alarm_gconf_disconnect: Removing listener %d from alarm #%d %p", priv->gconf_listener, alarm->id, alarm);
1269 gconf_client_notify_remove (priv->gconf_client, priv->gconf_listener);
1270 priv->gconf_listener = 0;
1271
1272 dir = alarm_gconf_get_dir (alarm);
1273 gconf_client_remove_dir (priv->gconf_client, dir, NULL);
1274 g_free (dir);
1275 }
1276 }
1277
1278 /*
1279 * Updates the local copy with the new value if it has changed.
1280 */
1281 static void
alarm_gconf_dir_changed(GConfClient * client,guint cnxn_id,GConfEntry * entry,gpointer data)1282 alarm_gconf_dir_changed (GConfClient *client,
1283 guint cnxn_id,
1284 GConfEntry *entry,
1285 gpointer data)
1286 {
1287 Alarm *alarm = ALARM (data);
1288 GParamSpec *param;
1289 gchar *name;
1290
1291 guint i;
1292 gboolean b;
1293 const gchar *str;
1294 GSList *list;
1295
1296 name = g_path_get_basename (entry->key);
1297 param = g_object_class_find_property (G_OBJECT_GET_CLASS (alarm), name);
1298
1299 if (!param) {
1300 g_free (name);
1301 return;
1302 }
1303
1304 g_debug ("Alarm(%p) #%d: gconf_dir_changed(): %s", alarm, alarm->id, name);
1305
1306 switch (param->param_id) {
1307 case PROP_TYPE:
1308 str = gconf_value_get_string (entry->value);
1309 i = alarm_type_from_string (str);
1310 if (i > 0 && i != alarm->type)
1311 g_object_set (alarm, name, i, NULL);
1312 break;
1313 case PROP_TIME:
1314 i = gconf_value_get_int (entry->value);
1315 if (i != alarm->time)
1316 g_object_set (alarm, name, i, NULL);
1317 break;
1318 case PROP_TIMESTAMP:
1319 i = gconf_value_get_int (entry->value);
1320 if (i != alarm->timestamp)
1321 g_object_set (alarm, name, i, NULL);
1322 break;
1323 case PROP_ACTIVE:
1324 b = gconf_value_get_bool (entry->value);
1325 if (b != alarm->active) {
1326 g_object_set (alarm, name, b, NULL);
1327 }
1328 break;
1329 case PROP_MESSAGE:
1330 str = gconf_value_get_string (entry->value);
1331 if (strcmp (str, alarm->message) != 0)
1332 g_object_set (alarm, name, str, NULL);
1333 break;
1334 case PROP_REPEAT:
1335 list = alarm_gconf_extract_list_string (entry->value);
1336
1337 i = alarm_repeat_from_list (list);
1338 if (i != alarm->repeat)
1339 g_object_set (alarm, name, i, NULL);
1340
1341 g_slist_free (list);
1342 break;
1343 case PROP_NOTIFY_TYPE:
1344 str = gconf_value_get_string (entry->value);
1345 i = alarm_notify_type_from_string (str);
1346 if (i > 0 && i != alarm->notify_type)
1347 g_object_set (alarm, name, i, NULL);
1348 break;
1349 case PROP_SOUND_FILE:
1350 str = gconf_value_get_string (entry->value);
1351 if (strcmp (str, alarm->sound_file) != 0)
1352 g_object_set (alarm, name, str, NULL);
1353 break;
1354 case PROP_SOUND_LOOP:
1355 b = gconf_value_get_bool (entry->value);
1356 if (b != alarm->sound_loop)
1357 g_object_set (alarm, name, b, NULL);
1358 break;
1359 case PROP_COMMAND:
1360 str = gconf_value_get_string (entry->value);
1361 if (strcmp (str, alarm->command) != 0)
1362 g_object_set (alarm, name, str, NULL);
1363 break;
1364 default:
1365 g_warning ("Alarm(%p) #%d: gconf_dir_changed(): Property ID %d not handled!",
1366 alarm, alarm->id, param->param_id);
1367 break;
1368 }
1369
1370 g_free (name);
1371 }
1372
1373 static void
alarm_dispose(GObject * object)1374 alarm_dispose (GObject *object)
1375 {
1376 Alarm *alarm = ALARM (object);
1377 // AlarmPrivate *priv = ALARM_PRIVATE (object);
1378 GObjectClass *parent = (GObjectClass *)alarm_parent_class;
1379
1380 g_debug ("Alarm(%p) #%d: dispose()", alarm, alarm->id);
1381
1382 if (parent->dispose)
1383 parent->dispose (object);
1384
1385 alarm_gconf_disconnect (alarm);
1386 alarm_timer_remove(alarm);
1387 alarm_clear (alarm);
1388 }
1389
1390 /*
1391 * Convenience function for creating a new alarm instance.
1392 * Passing -1 as the id will generate a new ID with alarm_gen_id
1393 */
1394 Alarm *
alarm_new(const gchar * gconf_dir,gint id)1395 alarm_new (const gchar *gconf_dir, gint id)
1396 {
1397 Alarm *alarm;
1398
1399 if (id < 0) {
1400 id = alarm_gen_id_dir (gconf_dir);
1401 }
1402
1403 alarm = g_object_new (TYPE_ALARM,
1404 "gconf-dir", gconf_dir,
1405 "id", id,
1406 NULL);
1407
1408 return alarm;
1409 }
1410
1411 guint
alarm_gen_id_dir(const gchar * gconf_dir)1412 alarm_gen_id_dir (const gchar *gconf_dir)
1413 {
1414 GConfClient *client;
1415 gchar *key = NULL;
1416 gint id;
1417
1418 client = gconf_client_get_default ();
1419
1420 id = 0;
1421 do {
1422 if (key)
1423 g_free (key);
1424
1425 key = g_strdup_printf("%s/" ALARM_GCONF_DIR_PREFIX "%d", gconf_dir, id);
1426 id++;
1427
1428 } while (gconf_client_dir_exists (client, key, NULL));
1429
1430 g_free (key);
1431
1432 return (guint)(id-1);
1433 }
1434
1435 /*
1436 * Will try to find the first available ID in gconf_dir
1437 */
1438 guint
alarm_gen_id(Alarm * alarm)1439 alarm_gen_id (Alarm *alarm)
1440 {
1441 return alarm_gen_id_dir (alarm->gconf_dir);
1442 }
1443
alarm_type_to_string(AlarmType type)1444 const gchar *alarm_type_to_string (AlarmType type)
1445 {
1446 return gconf_enum_to_string (alarm_type_enum_map, type);
1447 }
1448
alarm_type_from_string(const gchar * type)1449 AlarmType alarm_type_from_string (const gchar *type)
1450 {
1451 AlarmType ret = ALARM_TYPE_INVALID;
1452
1453 if (!type)
1454 return ret;
1455
1456 gconf_string_to_enum (alarm_type_enum_map, type, (gint *)&ret);
1457
1458 return ret;
1459 }
1460
alarm_notify_type_to_string(AlarmNotifyType type)1461 const gchar *alarm_notify_type_to_string (AlarmNotifyType type)
1462 {
1463 return gconf_enum_to_string (alarm_notify_type_enum_map, type);
1464 }
1465
alarm_notify_type_from_string(const gchar * type)1466 AlarmNotifyType alarm_notify_type_from_string (const gchar *type)
1467 {
1468 AlarmNotifyType ret = ALARM_NOTIFY_INVALID;
1469
1470 if (!type)
1471 return ret;
1472
1473 gconf_string_to_enum (alarm_notify_type_enum_map, type, (gint *)&ret);
1474
1475 return ret;
1476 }
1477
1478 gchar *
alarm_gconf_get_dir(Alarm * alarm)1479 alarm_gconf_get_dir (Alarm *alarm)
1480 {
1481 gchar *key;
1482
1483 g_return_val_if_fail (IS_ALARM (alarm), NULL);
1484
1485 key = g_strdup_printf ("%s/" ALARM_GCONF_DIR_PREFIX "%d", alarm->gconf_dir, alarm->id);
1486
1487 return key;
1488 }
1489
1490 gchar *
alarm_gconf_get_full_key(Alarm * alarm,const gchar * key)1491 alarm_gconf_get_full_key (Alarm *alarm, const gchar *key)
1492 {
1493 gchar *gconf_key;
1494 gchar *full_key;
1495
1496 g_return_val_if_fail (IS_ALARM (alarm), NULL);
1497
1498 if (!key)
1499 return NULL;
1500
1501 // Replace dashes with underscores
1502 gconf_key = g_strdup (key);
1503 g_strcanon (gconf_key, "abcdefghijklmnopqrstuvwxyz", '_');
1504
1505 full_key = g_strdup_printf ("%s/" ALARM_GCONF_DIR_PREFIX "%d/%s",
1506 alarm->gconf_dir, alarm->id, gconf_key);
1507
1508 g_free (gconf_key);
1509
1510 return full_key;
1511 }
1512
1513 /**
1514 * Compare two alarms based on ID
1515 */
1516 static gint
alarm_list_item_compare(gconstpointer a,gconstpointer b)1517 alarm_list_item_compare (gconstpointer a, gconstpointer b)
1518 {
1519 Alarm *a1 = ALARM (a);
1520 Alarm *a2 = ALARM (b);
1521
1522 return a1->id - a2->id;
1523 }
1524
1525 /*
1526 * Check if a gconf directory is a valid alarm dir.
1527 *
1528 * Returns >= 0 for valid alarm directory, -1 otherwise.
1529 */
1530 gint
alarm_gconf_dir_get_id(const gchar * dir)1531 alarm_gconf_dir_get_id (const gchar *dir)
1532 {
1533 gint id;
1534 gchar *d;
1535
1536 d = g_path_get_basename (dir);
1537
1538 if (sscanf (d, ALARM_GCONF_DIR_PREFIX "%d", &id) <= 0 || id < 0) {
1539 // INVALID
1540 id = -1;
1541 }
1542
1543 g_free (d);
1544
1545 return id;
1546 }
1547
1548 /*
1549 * Get list of alarms in gconf_dir
1550 */
1551 GList *
alarm_get_list(const gchar * gconf_dir)1552 alarm_get_list (const gchar *gconf_dir)
1553 {
1554 GConfClient *client;
1555 GSList *dirs = NULL;
1556 GSList *l = NULL;
1557 gchar *tmp;
1558
1559 GList *ret = NULL;
1560 Alarm *alarm;
1561 gint id;
1562
1563 client = gconf_client_get_default ();
1564 dirs = gconf_client_all_dirs (client, gconf_dir, NULL);
1565
1566 if (!dirs)
1567 return NULL;
1568
1569 for (l = dirs; l; l = l->next) {
1570 tmp = (gchar *)l->data;
1571
1572 id = alarm_gconf_dir_get_id (tmp);
1573 if (id >= 0) {
1574 g_debug ("Alarm: get_list() found '%s' #%d", tmp, id);
1575
1576 alarm = alarm_new (gconf_dir, id);
1577 // g_debug ("\tref = %d", G_OBJECT (alarm)->ref_count);
1578 ret = g_list_insert_sorted (ret, alarm, alarm_list_item_compare);
1579 // g_debug ("\tappend ref = %d", G_OBJECT (alarm)->ref_count);
1580 }
1581
1582 g_free (tmp);
1583 }
1584
1585 g_slist_free (dirs);
1586
1587 return ret;
1588 }
1589
1590 /*
1591 * Connect a signal callback to all alarms in list.
1592 */
1593 void
alarm_signal_connect_list(GList * instances,const gchar * detailed_signal,GCallback c_handler,gpointer data)1594 alarm_signal_connect_list (GList *instances,
1595 const gchar *detailed_signal,
1596 GCallback c_handler,
1597 gpointer data)
1598 {
1599 GList *l;
1600 Alarm *a;
1601 g_debug ("Alarm: signal_connect_list()");
1602 for (l = instances; l != NULL; l = l->next) {
1603 a = ALARM (l->data);
1604
1605 g_debug ("\tconnecting Alarm(%p) #%d: %s...", a, a->id, detailed_signal);
1606
1607 g_signal_connect (a, detailed_signal, c_handler, data);
1608 }
1609 }
1610
1611
1612 /**
1613 * Player error
1614 */
1615 static void
alarm_player_error_cb(MediaPlayer * player,GError * err,gpointer data)1616 alarm_player_error_cb (MediaPlayer *player, GError *err, gpointer data)
1617 {
1618 Alarm *alarm = ALARM (data);
1619 gchar *uri;
1620 gchar *msg;
1621
1622 uri = media_player_get_uri (player);
1623 msg = g_strdup_printf ("Could not play '%s': %s", uri, err->message);
1624
1625 g_critical ("%s", msg);
1626
1627 /* Emit error signal */
1628 alarm_error_trigger (alarm, ALARM_ERROR_PLAY, msg);
1629
1630 g_free (uri);
1631 g_free (msg);
1632 }
1633
1634 static void
alarm_player_state_cb(MediaPlayer * player,MediaPlayerState state,gpointer data)1635 alarm_player_state_cb (MediaPlayer *player, MediaPlayerState state, gpointer data)
1636 {
1637 static MediaPlayerState prev_state = MEDIA_PLAYER_INVALID;
1638
1639 Alarm *alarm = ALARM (data);
1640 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1641
1642 if (state != prev_state) {
1643 // Emit player_changed signal
1644 g_signal_emit (alarm, alarm_signal[SIGNAL_PLAYER], 0, state, NULL);
1645 }
1646
1647 if (state == MEDIA_PLAYER_STOPPED) {
1648 g_debug ("Alarm(%p) #%d: Freeing media player %p", alarm, alarm->id, player);
1649
1650 media_player_free (player);
1651
1652 priv->player = NULL;
1653 }
1654 }
1655
1656 static gboolean
alarm_player_timeout(gpointer data)1657 alarm_player_timeout (gpointer data)
1658 {
1659 Alarm *alarm = ALARM (data);
1660
1661 g_debug ("Alarm(%p) #%d: player_timeout", alarm, alarm->id);
1662
1663 alarm_player_stop (alarm);
1664
1665 return FALSE;
1666 }
1667
1668
1669 /**
1670 * Play sound via gstreamer
1671 */
1672 static void
alarm_player_start(Alarm * alarm)1673 alarm_player_start (Alarm *alarm)
1674 {
1675 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1676
1677 if (priv->player == NULL) {
1678 priv->player = media_player_new (alarm->sound_file,
1679 alarm->sound_loop,
1680 alarm_player_state_cb, alarm,
1681 alarm_player_error_cb, alarm);
1682 if (priv->player == NULL) {
1683 // Unable to create player
1684 alarm_error_trigger (alarm, ALARM_ERROR_PLAY,
1685 _("Could not create player! Please check your sound settings."));
1686 return;
1687 }
1688 } else {
1689 media_player_set_uri (priv->player, alarm->sound_file);
1690 }
1691
1692 media_player_start (priv->player);
1693
1694 g_debug ("Alarm(%p) #%d: player_start...", alarm, alarm->id);
1695
1696 /*
1697 * Add stop timeout
1698 */
1699 priv->player_timer_id = g_timeout_add_seconds(ALARM_SOUND_TIMEOUT, alarm_player_timeout, alarm);
1700 }
1701
1702 /**
1703 * Stop player
1704 */
1705 static void
alarm_player_stop(Alarm * alarm)1706 alarm_player_stop (Alarm *alarm)
1707 {
1708 AlarmPrivate *priv = ALARM_PRIVATE (alarm);
1709
1710 if (priv->player != NULL) {
1711 media_player_stop (priv->player);
1712
1713 if (priv->player_timer_id > 0) {
1714 g_source_remove (priv->player_timer_id);
1715 priv->player_timer_id = 0;
1716 }
1717 }
1718 }
1719
1720 /*
1721 * Run Command
1722 */
1723 static void
alarm_command_run(Alarm * alarm)1724 alarm_command_run (Alarm *alarm)
1725 {
1726 GError *err = NULL;
1727 gchar *msg;
1728
1729 if (!g_spawn_command_line_async (alarm->command, &err)) {
1730
1731 msg = g_strdup_printf ("Could not launch `%s': %s", alarm->command, err->message);
1732
1733 g_critical ("%s", msg);
1734
1735 /* Emit error signal */
1736 alarm_error_trigger (alarm, ALARM_ERROR_COMMAND, msg);
1737
1738 g_free (msg);
1739 g_error_free (err);
1740 }
1741 }
1742
1743
1744
1745
1746
1747
1748 /*
1749 * Set time according to hour, min, sec
1750 */
1751 void
alarm_set_time(Alarm * alarm,guint hour,guint minute,guint second)1752 alarm_set_time (Alarm *alarm, guint hour, guint minute, guint second)
1753 {
1754 g_debug ("Alarm(%p) #%d: set_time (%d:%d:%d)", alarm, alarm->id,
1755 hour, minute, second);
1756
1757 g_object_set (alarm, "time", second + minute * 60 + hour * 60 * 60, NULL);
1758 }
1759
1760 /*
1761 * Calculates the distance from wday1 to wday2
1762 */
1763 gint
alarm_wday_distance(gint wday1,gint wday2)1764 alarm_wday_distance (gint wday1, gint wday2)
1765 {
1766 gint d;
1767
1768 d = wday2 - wday1;
1769 if (d < 0)
1770 d += 7;
1771
1772 return d;
1773 }
1774
1775 static gboolean
alarm_time_is_future(struct tm * tm,guint hour,guint minute,guint second)1776 alarm_time_is_future (struct tm *tm, guint hour, guint minute, guint second)
1777 {
1778 return (hour > tm->tm_hour ||
1779 (hour == tm->tm_hour && minute > tm->tm_min) ||
1780 (hour == tm->tm_hour && minute == tm->tm_min && second > tm->tm_sec));
1781 }
1782
1783
1784 /*
1785 * Set time according to hour, min, sec and alarm->repeat
1786 */
1787 static void
alarm_set_timestamp(Alarm * alarm,guint hour,guint minute,guint second)1788 alarm_set_timestamp (Alarm *alarm, guint hour, guint minute, guint second)
1789 {
1790 time_t now, new;
1791 gint i, d, wday;
1792 AlarmRepeat rep;
1793 struct tm *tm;
1794
1795 g_debug ("Alarm(%p) #%d: set_timestamp (%d, %d, %d)", alarm, alarm->id,
1796 hour, minute, second);
1797
1798 time (&now);
1799 tm = localtime (&now);
1800
1801 // Automatically detect Daylight Savings Time (DST)
1802 tm->tm_isdst = -1;
1803
1804 if (alarm->repeat == ALARM_REPEAT_NONE) {
1805 // Check if the alarm is for tomorrow
1806 if (!alarm_time_is_future (tm, hour, minute, second)) {
1807
1808 g_debug("\tAlarm is for tomorrow.");
1809 tm->tm_mday++;
1810 }
1811 } else {
1812 // REPEAT SET: Find the closest repeat day
1813 wday = -1;
1814
1815 i = tm->tm_wday;
1816
1817 // Try finding a day in this week
1818 for (; i < 7; i++) {
1819 rep = 1 << i;
1820 if (alarm->repeat & rep) {
1821 if (i == tm->tm_wday && !alarm_time_is_future (tm, hour, minute, second)) continue;
1822 wday = i;
1823 break;
1824 }
1825 }
1826
1827 // If we haven't found a day in the current week, check next week
1828 if (wday == -1) {
1829 for (i = 0; i <= tm->tm_wday; i++) {
1830 rep = 1 << i;
1831 if (alarm->repeat & rep) {
1832 wday = i;
1833 break;
1834 }
1835 }
1836 }
1837
1838 g_debug ("Closest WDAY = %d", wday);
1839
1840 if (wday == tm->tm_wday && (!alarm_time_is_future (tm, hour, minute, second)))
1841 wday = 7;
1842
1843 // Calculate distance from now to wday
1844 if (wday == 7) {
1845 g_debug("\tAlarm is in (forced) 1 week.");
1846 d = 7;
1847 } else {
1848 d = alarm_wday_distance (tm->tm_wday, wday);
1849 }
1850
1851 g_debug ("\tAlarm is in %d days.", d);
1852
1853 tm->tm_mday += d;
1854 }
1855
1856 tm->tm_hour = hour;
1857 tm->tm_min = minute;
1858 tm->tm_sec = second;
1859
1860 new = mktime (tm);
1861 g_debug ("\tSetting to %d", (gint) new);
1862 g_object_set (alarm, "timestamp", new, NULL);
1863 }
1864
1865 /*
1866 * Update the alarm timestamp to point to the nearest future
1867 * hour/min/sec according to the time value.
1868 */
1869 void
alarm_update_timestamp(Alarm * alarm)1870 alarm_update_timestamp (Alarm *alarm)
1871 {
1872 if (alarm->type == ALARM_TYPE_CLOCK) {
1873 struct tm *tm = alarm_get_time (alarm);
1874 g_debug ("Alarm(%p) #%d: update_timestamp_full: %d:%d:%d", alarm, alarm->id,
1875 tm->tm_hour, tm->tm_min, tm->tm_sec);
1876 alarm_set_timestamp (alarm, tm->tm_hour, tm->tm_min, tm->tm_sec);
1877 } else {
1878 /* ALARM_TYPE_TIMER */
1879 g_object_set (alarm, "timestamp", time(NULL) + alarm->time, NULL);
1880 }
1881 }
1882
1883 /*
1884 * Get the alarm time.
1885 */
1886 struct tm *
alarm_get_time(Alarm * alarm)1887 alarm_get_time (Alarm *alarm)
1888 {
1889 return gmtime (&(alarm->time));
1890 }
1891
1892 static struct tm tm;
1893
1894 /**
1895 * Get the remaining alarm time.
1896 *
1897 * The return value is a direct pointer to an internal struct and must NOT
1898 * be freed. The return value also changes for every call to alarm_get_remain,
1899 * so copy it if needed.
1900 */
1901 struct tm *
alarm_get_remain(Alarm * alarm)1902 alarm_get_remain (Alarm *alarm)
1903 {
1904 time_t now;
1905 //struct tm tm;
1906
1907 now = time (NULL);
1908 tm.tm_sec = alarm->timestamp - now;
1909
1910 tm.tm_min = tm.tm_sec / 60;
1911 tm.tm_sec -= tm.tm_min * 60;
1912
1913 tm.tm_hour = tm.tm_min / 60;
1914 tm.tm_min -= tm.tm_hour * 60;
1915
1916 return &tm;
1917 }
1918
1919 /**
1920 * Get the remaining alarm time in seconds
1921 */
1922 time_t
alarm_get_remain_seconds(Alarm * alarm)1923 alarm_get_remain_seconds (Alarm *alarm)
1924 {
1925 time_t now = time (NULL);
1926
1927 return alarm->timestamp - now;
1928 }
1929
1930
1931 /*
1932 * AlarmRepeat utilities {{
1933 */
1934
1935 const gchar *
alarm_repeat_to_string(AlarmRepeat repeat)1936 alarm_repeat_to_string (AlarmRepeat repeat)
1937 {
1938 return gconf_enum_to_string (alarm_repeat_enum_map, repeat);
1939 }
1940
1941 AlarmRepeat
alarm_repeat_from_string(const gchar * str)1942 alarm_repeat_from_string (const gchar *str)
1943 {
1944 AlarmRepeat ret = ALARM_REPEAT_NONE;
1945
1946 if (!str)
1947 return ret;
1948
1949 gconf_string_to_enum (alarm_repeat_enum_map, str, (gint *)&ret);
1950
1951 return ret;
1952 }
1953
1954 AlarmRepeat
alarm_repeat_from_list(GSList * list)1955 alarm_repeat_from_list (GSList *list)
1956 {
1957 GSList *l;
1958 AlarmRepeat repeat = ALARM_REPEAT_NONE;
1959
1960 for (l = list; l; l = l->next) {
1961 repeat |= alarm_repeat_from_string ((gchar *)l->data);
1962 }
1963
1964 return repeat;
1965 }
1966
1967 GSList *
alarm_repeat_to_list(AlarmRepeat repeat)1968 alarm_repeat_to_list (AlarmRepeat repeat)
1969 {
1970 GSList *list = NULL;
1971 AlarmRepeat r;
1972 gint i;
1973
1974 for (r = ALARM_REPEAT_SUN, i = 0; r <= ALARM_REPEAT_SAT; r = 1 << ++i) {
1975 if (repeat & r)
1976 list = g_slist_append (list, (gpointer) alarm_repeat_to_string (r));
1977 }
1978
1979 return list;
1980 }
1981
1982 gboolean
alarm_should_repeat(Alarm * alarm)1983 alarm_should_repeat (Alarm *alarm)
1984 {
1985 return alarm->type == ALARM_TYPE_CLOCK && alarm->repeat != ALARM_REPEAT_NONE;
1986 }
1987
1988 /*
1989 * Create a pretty string representation of AlarmRepeat
1990 *
1991 * The return value must be freed afterwards
1992 */
1993 gchar *
alarm_repeat_to_pretty(AlarmRepeat repeat)1994 alarm_repeat_to_pretty (AlarmRepeat repeat)
1995 {
1996 gchar *str;
1997
1998 GString *s;
1999 struct tm tm;
2000 gchar tmp[20];
2001 AlarmRepeat r;
2002 gint i;
2003
2004 switch (repeat) {
2005 case ALARM_REPEAT_NONE:
2006 str = g_strdup (_("Once"));
2007 break;
2008 case ALARM_REPEAT_WEEKDAYS:
2009 str = g_strdup (_("Weekdays"));
2010 break;
2011 case ALARM_REPEAT_WEEKENDS:
2012 str = g_strdup (_("Weekends"));
2013 break;
2014 case ALARM_REPEAT_ALL:
2015 str = g_strdup (_("Every day"));
2016 break;
2017 default:
2018 // Custom repeat, create a list of weekdays
2019 s = g_string_new ("");
2020
2021 for (r = ALARM_REPEAT_SUN, i = 0; r <= ALARM_REPEAT_SAT; r = 1 << ++i) {
2022 if (repeat & r) {
2023 tm.tm_wday = i;
2024 strftime (tmp, sizeof(tmp), "%a", &tm);
2025 g_string_append_printf (s, "%s, ", tmp);
2026 }
2027 }
2028
2029 g_string_truncate (s, s->len - 2);
2030
2031 str = s->str;
2032
2033 g_string_free (s, FALSE);
2034 break;
2035 }
2036
2037 return str;
2038 }
2039
2040 /*
2041 * }} AlarmRepeat utilities
2042 */
2043