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