2
* alarms-list.d -- List alarms dialog
4
* Copyright (C) 2007-2008 Johannes H. Jensen <joh@pseudoberries.com>
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
* Johannes H. Jensen <joh@pseudoberries.com>
24
#include "alarms-list.h"
25
#include "edit-alarm.h"
28
list_alarms_toggled_cb (GtkCellRendererToggle *cell_renderer,
32
GtkListStore *model = GTK_LIST_STORE (data);
36
gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (model), &iter, path);
37
gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &alarm, -1);
39
g_debug ("list_alarms_toggled %s: #%d", path, alarm->id);
41
alarm_set_enabled (alarm, !(alarm->active));
44
* gtk_tree_model_get () will increase the reference count
45
* of the alarms each time it's called. We dereference it
46
* here so they can be properly freed later with g_object_unref()
48
g_object_unref (alarm);
51
typedef struct _AlarmModelChangedArg {
55
} AlarmModelChangedArg;
58
list_alarms_clear_args (AlarmApplet *applet)
61
AlarmModelChangedArg *arg;
64
* Free old list of arguments
66
for (l = applet->list_alarms_args; l; l = l->next) {
67
arg = (AlarmModelChangedArg *)l->data;
69
// Remove any callbacks
70
g_source_remove_by_user_data (arg);
76
g_list_free (applet->list_alarms_args);
77
applet->list_alarms_args = NULL;
81
list_alarms_update_timer (gpointer data)
83
AlarmModelChangedArg *arg = (AlarmModelChangedArg *)data;
86
if (!GTK_IS_TREE_MODEL (arg->store)) {
87
// Dialog is closed, free args
92
if (!arg->alarm->active)
95
path = gtk_tree_model_get_path (GTK_TREE_MODEL (arg->store), &(arg->iter));
97
gtk_tree_model_row_changed (GTK_TREE_MODEL (arg->store), path, &(arg->iter));
99
gtk_tree_path_free (path);
103
list_alarms_add_timer (AlarmModelChangedArg *arg)
105
g_source_remove_by_user_data (arg);
106
g_timeout_add_seconds (1, (GSourceFunc) list_alarms_update_timer, arg);
110
alarm_model_changed (AlarmModelChangedArg *arg)
114
// TODO: Only update on the actual fields we're interested?
116
path = gtk_tree_model_get_path (GTK_TREE_MODEL (arg->store), &(arg->iter));
118
gtk_tree_model_row_changed (GTK_TREE_MODEL (arg->store), path, &(arg->iter));
120
gtk_tree_path_free (path);
124
* Callback for when an alarm changes
125
* Emits model-row-changed
128
alarm_changed (GObject *object,
132
AlarmModelChangedArg *arg = (AlarmModelChangedArg *)data;
133
Alarm *a = ALARM (object);
135
alarm_model_changed (arg);
137
// If alarm is active, update the view every second for remaining time
138
if (strcmp (param->name, "active") == 0 && a->active) {
139
list_alarms_add_timer (arg);
144
* Callback for when the alarm player changes
147
alarm_player_changed (Alarm *a, MediaPlayerState state, gpointer data)
149
g_debug ("alarm_player_changed: %d", state);
150
alarm_model_changed ((AlarmModelChangedArg *)data);
154
* Update list of alarms (VIEW)
157
list_alarms_update (AlarmApplet *applet)
159
GtkListStore *store = applet->list_alarms_store;
161
GList *l = NULL, *args = NULL;
163
AlarmModelChangedArg *arg;
165
g_debug ("list_alarms_update ()");
172
gtk_list_store_clear (store);
177
for (l = applet->alarms; l; l = l->next) {
180
gtk_list_store_append (store, &iter);
182
gtk_list_store_set (store, &iter, 0, a, -1);
185
arg = g_new (AlarmModelChangedArg, 1);
190
// Append to argument list
191
args = g_list_append (args, arg);
193
// Disconnect old handlers, if any
194
g_signal_handlers_disconnect_matched (a, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, alarm_changed, NULL);
196
// Notify us of changes to the alarm
197
g_signal_connect (a, "notify", G_CALLBACK (alarm_changed), arg);
199
// Notify us of changes to the alarm player
200
g_signal_connect (a, "player_changed", G_CALLBACK (alarm_player_changed), arg);
202
// If alarm is active, update the view every second for remaining time
204
list_alarms_add_timer (arg);
207
g_print ("Alarm #%d (%p): %s [ref=%d]\n", a->id, a, a->message, G_OBJECT (a)->ref_count);
210
/* Clear old arguments */
211
list_alarms_clear_args (applet);
213
/* Update with new list of args */
214
applet->list_alarms_args = args;
218
alarm_update_renderer (GtkTreeViewColumn *tree_column,
219
GtkCellRenderer *renderer,
224
AlarmColumn col = (AlarmColumn)data;
226
time_t hour, min, sec, now;
230
gtk_tree_model_get (model, iter, 0, &a, -1);
234
if (a->type == ALARM_TYPE_TIMER) {
235
g_object_set (renderer, "icon-name", TIMER_ICON, NULL);
237
g_object_set (renderer, "icon-name", ALARM_ICON, NULL);
241
g_object_set (renderer, "active", a->active, NULL);
244
/* If alarm is running (active), show remaining time */
246
tm = alarm_get_remain (a);
248
tm = alarm_get_time (a);
251
tmp = g_strdup_printf(_("%02d:%02d:%02d"), tm->tm_hour, tm->tm_min, tm->tm_sec);
253
g_object_set (renderer, "text", tmp, NULL);
258
g_object_set (renderer, "text", a->message, NULL);
261
if (alarm_is_playing (a)) {
262
g_object_set (renderer, "icon-name", "audio-volume-high", NULL);
264
g_object_set (renderer, "icon-name", NULL, NULL);
272
* gtk_tree_model_get () will increase the reference count
273
* of the alarms each time it's called. We dereference it
274
* here so they can be properly freed later with g_object_unref()
278
//g_debug ("alarm_update_render: alarm #%d (%p), col %d, ref=%d", a->id, a, col, G_OBJECT (a)->ref_count);
282
list_alarms_dialog_response_cb (GtkDialog *dialog,
286
list_alarms_dialog_close (applet);
290
add_button_cb (GtkButton *button, gpointer data)
293
AlarmApplet *applet = (AlarmApplet *)data;
294
AlarmListEntry *entry;
297
* Create new alarm, will fall back to defaults.
299
alarm = alarm_new (ALARM_GCONF_DIR, -1);
302
* Set first sound / app in list
304
if (applet->sounds != NULL) {
305
entry = (AlarmListEntry *)applet->sounds->data;
306
g_object_set (alarm, "sound-file", entry->data, NULL);
309
if (applet->apps != NULL) {
310
entry = (AlarmListEntry *)applet->apps->data;
311
g_object_set (alarm, "command", entry->data, NULL);
314
// Add alarm to list of alarms
315
alarm_applet_alarms_add (applet, alarm);
317
// Show edit alarm dialog
318
display_edit_alarm_dialog (applet, alarm);
321
* Update alarms list view
323
list_alarms_update (applet);
327
get_selected_alarm (AlarmApplet *applet)
329
GtkTreeSelection *selection;
335
g_assert (applet->list_alarms_view);
337
/* Fetch selection */
338
selection = gtk_tree_view_get_selection (applet->list_alarms_view);
340
if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
341
/* No alarms selected */
342
g_debug ("get_selected_alarm: No alarms selected");
346
gtk_tree_model_get (model, &iter, 0, &a, -1);
349
* gtk_tree_model_get () will increase the reference count
350
* of the alarms each time it's called. We dereference it
351
* here so they can be properly freed later with g_object_unref()
359
edit_button_cb (GtkButton *button, gpointer data)
361
AlarmApplet *applet = (AlarmApplet *)data;
362
Alarm *a = get_selected_alarm (applet);
365
/* No alarms selected */
369
// Clear any running alarms
372
display_edit_alarm_dialog (applet, a);
376
delete_button_cb (GtkButton *button, gpointer data)
378
AlarmApplet *applet = (AlarmApplet *)data;
379
Alarm *a = get_selected_alarm (applet);
385
/* No alarms selected */
389
// Clear any running alarms
392
dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (applet->list_alarms_dialog),
393
GTK_DIALOG_DESTROY_WITH_PARENT,
394
GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
395
_("<big><b>Delete alarm '%s'?</b></big>"),
397
g_object_set (dialog,
398
"title", _("Delete Alarm?"),
399
"icon-name", ALARM_ICON,
402
gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
403
_("Are you sure you want to delete\nalarm <b>#%d</b> labeled '<b>%s</b>'?"),
406
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
407
gtk_dialog_add_buttons(GTK_DIALOG (dialog),
408
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
409
GTK_STOCK_DELETE, GTK_RESPONSE_ACCEPT,
411
response = gtk_dialog_run (GTK_DIALOG (dialog));
412
gtk_widget_destroy (dialog);
414
if (response == GTK_RESPONSE_ACCEPT) {
415
g_debug ("delete_button_cb: GTK_RESPONSE_ACCEPT");
422
/* If there's a settings dialog open for this
425
AlarmSettingsDialog *sdialog = g_hash_table_lookup (applet->edit_alarm_dialogs, (gconstpointer)a->id);
427
alarm_settings_dialog_close (sdialog);
431
* Remove from applet list
433
alarm_applet_alarms_remove (applet, a);
436
* Fill store with alarms
438
list_alarms_update (applet);
440
g_debug ("delete_button_cb: GTK_RESPONSE_CANCEL");
445
list_alarm_selected_cb (GtkTreeView *view,
447
GtkTreeViewColumn *column,
453
AlarmApplet *applet = (AlarmApplet *)data;
455
model = gtk_tree_view_get_model (view);
457
if (!gtk_tree_model_get_iter (model, &iter, path)) {
458
g_warning ("Could not get iter.");
462
gtk_tree_model_get (model, &iter, 0, &a, -1);
465
* gtk_tree_model_get () will increase the reference count
466
* of the alarms each time it's called. We dereference it
467
* here so they can be properly freed later with g_object_unref()
471
// Clear any running alarms
474
display_edit_alarm_dialog (applet, a);
478
list_alarms_dialog_close (AlarmApplet *applet)
483
g_assert (applet->list_alarms_dialog);
486
gtk_widget_destroy (GTK_WIDGET (applet->list_alarms_dialog));
487
applet->list_alarms_dialog = NULL;
490
g_object_unref (applet->list_alarms_store);
491
applet->list_alarms_store = NULL;
493
/* g_object_unref (applet->list_alarms_view);*/
494
applet->list_alarms_view = NULL;
496
// Disconnect notify handlers
497
for (l = applet->alarms; l != NULL; l = l->next) {
499
g_signal_handlers_disconnect_matched (a, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, alarm_changed, NULL);
500
g_signal_handlers_disconnect_matched (a, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, alarm_player_changed, NULL);
503
/* Clear old arguments */
504
list_alarms_clear_args (applet);
508
list_alarms_dialog_display (AlarmApplet *applet)
510
if (applet->list_alarms_dialog != NULL) {
511
// Dialog already open.
512
gtk_window_present (GTK_WINDOW (applet->list_alarms_dialog));
517
GList *l; /* List of alarms */
523
GdkPixbuf *type_alarm_icon, *type_timer_icon;
524
GtkCellRenderer *type_renderer, *active_renderer,
525
*time_renderer, *label_renderer,
528
GtkTreeViewColumn *type_col, *active_col,
529
*time_col, *label_col,
533
GtkButton *add_button, *edit_button, *delete_button;
538
ui = glade_xml_new (ALARM_UI_XML, "list-alarms", NULL);
540
applet->list_alarms_dialog = GTK_DIALOG (glade_xml_get_widget (ui, "list-alarms"));
541
view = GTK_TREE_VIEW (glade_xml_get_widget (ui, "list-alarms-view"));
542
applet->list_alarms_view = view;
544
g_signal_connect (applet->list_alarms_dialog, "response",
545
G_CALLBACK (list_alarms_dialog_response_cb), applet);
548
add_button = GTK_BUTTON (glade_xml_get_widget (ui, "add-button"));
549
edit_button = GTK_BUTTON (glade_xml_get_widget (ui, "edit-button"));
550
delete_button = GTK_BUTTON (glade_xml_get_widget (ui, "delete-button"));
552
g_signal_connect (add_button, "clicked",
553
G_CALLBACK (add_button_cb), applet);
554
g_signal_connect (edit_button, "clicked",
555
G_CALLBACK (edit_button_cb), applet);
556
g_signal_connect (delete_button, "clicked",
557
G_CALLBACK (delete_button_cb), applet);
560
* Create list store model
562
store = gtk_list_store_new (1, TYPE_ALARM);
563
applet->list_alarms_store = store;
566
* Fill store with alarms
568
list_alarms_update (applet);
571
* Create view cell renderers
573
type_renderer = gtk_cell_renderer_pixbuf_new ();
575
active_renderer = gtk_cell_renderer_toggle_new ();
576
g_object_set (active_renderer,
577
/*"mode", GTK_CELL_RENDERER_MODE_EDITABLE,*/
580
g_signal_connect (active_renderer, "toggled", G_CALLBACK (list_alarms_toggled_cb), store);
582
time_renderer = gtk_cell_renderer_text_new ();
584
label_renderer = gtk_cell_renderer_text_new ();
585
g_object_set (label_renderer,
587
"mode", GTK_CELL_RENDERER_MODE_EDITABLE,
590
playing_renderer = gtk_cell_renderer_pixbuf_new ();
593
* Create view columns
595
type_col = gtk_tree_view_column_new_with_attributes(
596
_("Type"), type_renderer,
599
gtk_tree_view_column_set_cell_data_func (type_col, type_renderer,
600
alarm_update_renderer,
604
active_col = gtk_tree_view_column_new_with_attributes(
605
_("Active"), active_renderer,
608
gtk_tree_view_column_set_cell_data_func (active_col, active_renderer,
609
alarm_update_renderer,
610
ACTIVE_COLUMN, NULL);
613
time_col = gtk_tree_view_column_new_with_attributes(
614
_("Time"), time_renderer,
617
gtk_tree_view_column_set_cell_data_func(time_col, time_renderer,
618
alarm_update_renderer,
622
label_col = gtk_tree_view_column_new_with_attributes(
623
_("Label"), label_renderer,
626
gtk_tree_view_column_set_cell_data_func(label_col, label_renderer,
627
alarm_update_renderer,
630
playing_col = gtk_tree_view_column_new_with_attributes(
631
_("Playing"), playing_renderer,
634
gtk_tree_view_column_set_cell_data_func (playing_col, playing_renderer,
635
alarm_update_renderer,
636
PLAYING_COLUMN, NULL);
641
gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
643
gtk_tree_view_append_column (view, type_col);
644
gtk_tree_view_append_column (view, active_col);
645
gtk_tree_view_append_column (view, time_col);
646
gtk_tree_view_append_column (view, label_col);
647
gtk_tree_view_append_column (view, playing_col);
650
* Set up signal handlers
652
g_signal_connect (view, "row-activated", G_CALLBACK (list_alarm_selected_cb), applet);
654
// TODO: Calculate a good initial size for the window
656
//gtk_container_add (GTK_CONTAINER (list_alarms_box), GTK_WIDGET (view));
657
//gtk_box_pack_start_defaults (GTK_BOX (list_alarms_scrol), GTK_WIDGET (view));
659
gtk_widget_show_all (GTK_WIDGET (applet->list_alarms_dialog));