2
* alarms-list.d -- Alarm list window
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>
26
#include "alarm-list-window.h"
27
#include "alarm-settings.h"
28
#include "alarm-actions.h"
31
alarm_list_window_selection_changed (GtkTreeSelection *, gpointer);
34
alarm_list_window_update_timer (gpointer);
37
alarm_list_window_sort_iter_compare (GtkTreeModel *model,
38
GtkTreeIter *a, GtkTreeIter *b,
42
alarm_list_window_rows_reordered (GtkTreeModel *model,
49
alarm_list_window_enable_toggled (GtkCellRendererToggle *cell_renderer,
54
alarm_list_window_snooze_menu_activated (GtkMenuItem *item, gpointer data);
57
alarm_list_window_snooze_menu_custom_activated (GtkMenuItem *menuitem, gpointer data);
60
alarm_list_window_snooze_menu_update (AlarmListWindow *list_window);
63
alarm_list_window_update_row (AlarmListWindow *list_window, GtkTreeIter *iter);
66
* Create a new Alarm List Window
69
alarm_list_window_new (AlarmApplet *applet)
71
AlarmListWindow *list_window;
72
GtkBuilder *builder = applet->ui;
73
GtkTreeSelection *selection;
74
GtkTreeSortable *sortable;
77
list_window = g_new0 (AlarmListWindow, 1);
79
list_window->applet = applet;
82
list_window->window = GTK_WINDOW (gtk_builder_get_object (builder, "alarm-list-window"));
83
list_window->model = GTK_LIST_STORE (gtk_builder_get_object (builder, "alarms-liststore"));
84
list_window->tree_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "alarm-list-view"));
86
list_window->new_button = GTK_WIDGET (gtk_builder_get_object (builder, "new-button"));
87
list_window->edit_button = GTK_WIDGET (gtk_builder_get_object (builder, "edit-button"));
88
list_window->delete_button = GTK_WIDGET (gtk_builder_get_object (builder, "delete-button"));
89
list_window->enable_button = GTK_WIDGET (gtk_builder_get_object (builder, "enable-button"));
90
list_window->stop_button = GTK_WIDGET (gtk_builder_get_object (builder, "stop-button"));
91
list_window->snooze_button = GTK_WIDGET (gtk_builder_get_object (builder, "snooze-button"));
92
list_window->snooze_menu = GTK_WIDGET (gtk_builder_get_object (builder, "snooze-menu"));
94
// Set up window accelerator group
95
list_window->accel_group = gtk_accel_group_new ();
96
gtk_window_add_accel_group (list_window->window, list_window->accel_group);
98
// Connect some signals
99
selection = gtk_tree_view_get_selection (list_window->tree_view);
100
g_signal_connect (selection, "changed",
101
G_CALLBACK (alarm_list_window_selection_changed), applet);
103
// Update view every second for pretty countdowns
104
g_timeout_add (500, (GSourceFunc) alarm_list_window_update_timer, applet);
107
sortable = GTK_TREE_SORTABLE (list_window->model);
109
gtk_tree_sortable_set_sort_func (sortable, SORTID_TIME_REMAINING,
110
alarm_list_window_sort_iter_compare, GINT_TO_POINTER (SORTID_TIME_REMAINING),
113
// Set initial sort order
114
gtk_tree_sortable_set_sort_column_id (sortable, SORTID_TIME_REMAINING, GTK_SORT_ASCENDING);
116
// Populate with alarms
117
alarm_list_window_alarms_add (list_window, applet->alarms);
119
// Update snooze menu
120
alarm_list_window_snooze_menu_update (list_window);
126
* Show and present list window
129
alarm_list_window_show (AlarmListWindow *list_window)
131
gtk_window_present_with_time (list_window->window, gtk_get_current_event_time());
138
alarm_list_window_hide (AlarmListWindow *list_window)
140
gtk_widget_hide (GTK_WIDGET (list_window->window));
144
* Toggle visibility of list window
147
alarm_list_window_toggle (AlarmListWindow *list_window)
149
if (GTK_WIDGET_VISIBLE (list_window->window)) {
150
alarm_list_window_hide (list_window);
152
alarm_list_window_show (list_window);
163
* Find alarm in the model
165
* Returns TRUE if found and sets iter to the location
166
* Returns FALSE otherwise
169
alarm_list_window_find_alarm (GtkTreeModel *model,
177
valid = gtk_tree_model_get_iter_first (model, &it);
180
gtk_tree_model_get (model, &it, COLUMN_ALARM, &a, -1);
189
valid = gtk_tree_model_iter_next(model, &it);
196
* Check whether the list window contains an alarm
199
alarm_list_window_contains (AlarmListWindow *list_window, Alarm *alarm)
201
return alarm_list_window_find_alarm (GTK_TREE_MODEL (list_window->model), alarm, NULL);
205
* Update the row in the list at the position specified by iter
208
alarm_list_window_update_row (AlarmListWindow *list_window, GtkTreeIter *iter)
210
GtkTreeModel *model = GTK_TREE_MODEL (list_window->model);
217
const gchar *type_col;
218
const gchar *time_format;
222
// Get the alarm at iter
223
gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
227
// If alarm is running (active), show remaining time
229
tm = alarm_get_remain (a);
231
tm = alarm_get_time (a);
234
if (a->type == ALARM_TYPE_CLOCK) {
235
type_col = ALARM_ICON;
236
time_format = TIME_COL_CLOCK_FORMAT;
238
type_col = TIMER_ICON;
239
time_format = TIME_COL_TIMER_FORMAT;
242
// Create time column
243
strftime(tmp, sizeof(tmp), time_format, tm);
245
time_col = g_string_new (tmp);
246
if (a->type == ALARM_TYPE_CLOCK && a->repeat != ALARM_REPEAT_NONE) {
247
tmp2 = alarm_repeat_to_pretty (a->repeat);
248
g_string_append_printf (time_col, TIME_COL_REPEAT_FORMAT, tmp2);
252
// Create label column
253
tmp2 = g_markup_escape_text (a->message, -1);
255
label_col = g_strdup_printf (LABEL_COL_TRIGGERED_FORMAT, tmp2);
257
label_col = g_strdup_printf (LABEL_COL_FORMAT, tmp2);
261
gtk_list_store_set (GTK_LIST_STORE (model), iter,
262
COLUMN_TYPE, type_col,
263
COLUMN_TIME, time_col->str,
264
COLUMN_LABEL, label_col,
265
COLUMN_ACTIVE, a->active,
266
COLUMN_TRIGGERED, a->triggered,
269
// Restore icon visibility when an alarm is cleared / snoozed
271
gtk_list_store_set (GTK_LIST_STORE (model), iter, COLUMN_SHOW_ICON, TRUE, -1);
275
g_string_free (time_col, TRUE);
280
* Add alarm to the list window
283
alarm_list_window_alarm_add (AlarmListWindow *list_window, Alarm *alarm)
285
GtkListStore *store = list_window->model;
288
gtk_list_store_append (store, &iter);
289
gtk_list_store_set (store, &iter, COLUMN_ALARM, alarm, -1);
291
alarm_list_window_update_row (list_window, &iter);
295
* Update alarm in the list window
298
alarm_list_window_alarm_update (AlarmListWindow *list_window, Alarm *alarm)
302
g_debug ("AlarmListWindow alarm_update: %p (%s)", alarm, alarm->message);
304
if (alarm_list_window_find_alarm (GTK_TREE_MODEL (list_window->model), alarm, &iter)) {
305
alarm_list_window_update_row (list_window, &iter);
307
g_warning ("AlarmListWindow alarm_update: Could not find alarm %p", alarm);
312
* Remove alarm from the list window
315
alarm_list_window_alarm_remove (AlarmListWindow *list_window, Alarm *alarm)
319
if (alarm_list_window_find_alarm (GTK_TREE_MODEL (list_window->model), alarm, &iter)) {
320
gtk_list_store_remove (list_window->model, &iter);
322
g_warning ("AlarmListWindow alarm_remove: Could not find alarm %p", alarm);
327
* Add several alarms to the list window
330
alarm_list_window_alarms_add (AlarmListWindow *list_window, GList *alarms)
332
AlarmApplet *applet = list_window->applet;
337
for (l = applet->alarms; l; l = l->next) {
340
alarm_list_window_alarm_add (list_window, a);
345
* Update the alarm view every second
348
alarm_list_window_update_timer (gpointer data)
350
AlarmApplet *applet = (AlarmApplet *)data;
351
GtkTreeModel *model = GTK_TREE_MODEL (applet->list_window->model);
357
valid = gtk_tree_model_get_iter_first (model, &iter);
360
alarm_list_window_update_row (applet->list_window, &iter);
362
gtk_tree_model_get (model, &iter,
364
COLUMN_SHOW_ICON, &show_icon, -1);
366
// Blink icon on triggered alarms
368
gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_SHOW_ICON, !show_icon, -1);
371
valid = gtk_tree_model_iter_next(model, &iter);
379
* Sort compare function
382
alarm_list_window_sort_iter_compare (GtkTreeModel *model,
383
GtkTreeIter *i1, GtkTreeIter *i2,
386
gint sortcol = GPOINTER_TO_INT (data);
391
gtk_tree_model_get (model, i1, COLUMN_ALARM, &a1, -1);
392
gtk_tree_model_get (model, i2, COLUMN_ALARM, &a2, -1);
395
case SORTID_TIME_REMAINING:
397
// Sort by remaining time
400
// Show active alarms first
401
if (a1->active && a2->active) {
402
t1 = alarm_get_remain_seconds (a1);
403
t2 = alarm_get_remain_seconds (a2);
405
} else if (a1->active && !a2->active) {
408
} else if (!a1->active && a2->active) {
412
// Both inactive, sort by time
421
g_return_val_if_reached (0);
433
* Get the selected alarm
436
alarm_list_window_get_selected_alarm (AlarmListWindow *list_window)
439
GtkTreeSelection *selection;
443
g_assert (list_window);
446
selection = gtk_tree_view_get_selection (list_window->tree_view);
448
if (!gtk_tree_selection_get_selected(selection, &model, &iter)) {
449
// No alarms selected
450
//g_debug ("get_selected_alarm: No alarms selected!");
454
gtk_tree_model_get (model, &iter, COLUMN_ALARM, &a, -1);
456
// gtk_tree_model_get () will increase the reference count
457
// of the alarms each time it's called. We dereference it
458
// here so they can be properly freed later with g_object_unref()
459
// Ugh, we use gtk_tree_model_get a lot, is there no other way?
460
//g_object_unref (a);
466
* Set the selected alarm
469
alarm_list_window_select_alarm (AlarmListWindow *list_window, Alarm *alarm)
471
GtkTreeModel *model = GTK_TREE_MODEL (list_window->model);
472
GtkTreeSelection *selection;
475
if (!alarm_list_window_find_alarm (model, alarm, &iter)) {
476
g_warning ("AlarmListWindow select_alarm: Alarm %p not found!", alarm);
480
selection = gtk_tree_view_get_selection (list_window->tree_view);
482
gtk_tree_selection_select_iter (selection, &iter);
487
* Selection changed in tree view
489
* Here we update the associated actions
492
alarm_list_window_selection_changed (GtkTreeSelection *selection, gpointer data)
494
AlarmApplet *applet = (AlarmApplet *)data;
495
AlarmListWindow *list_window = applet->list_window;
496
Alarm *a = list_window->selected_alarm;
498
// If rows have just been reordered, retain the selected row
499
// But only if the reordering was triggered by a click on the toggle cell
500
if (list_window->reordered && list_window->toggled) {
501
list_window->reordered = FALSE;
502
list_window->toggled = FALSE;
504
alarm_list_window_select_alarm (list_window, list_window->selected_alarm);
508
// Reset reordered and toggled flags
509
list_window->reordered = FALSE;
510
list_window->toggled = FALSE;
513
alarm_applet_actions_update_sensitive (applet);
514
alarm_action_update_enabled (applet);
516
// Update selected alarm (might be NULL)
517
list_window->selected_alarm = alarm_list_window_get_selected_alarm (list_window);
519
if (list_window->selected_alarm) {
520
// Update snooze button menu
521
if (list_window->selected_alarm->type == ALARM_TYPE_CLOCK) {
522
// We always snooze for 9 mins on alarm clocks
523
gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (list_window->snooze_button), NULL);
525
// Allow custom snooze mins
526
gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (list_window->snooze_button), list_window->snooze_menu);
531
g_debug ("AlarmListWindow: selection-changed from %s (%p) to %s (%p)",
532
(a) ? a->message : "<none>", a,
533
(list_window->selected_alarm) ? list_window->selected_alarm->message : "<none>",
534
list_window->selected_alarm);
538
* Toggle cell changed
541
alarm_list_window_enable_toggled (GtkCellRendererToggle *cell_renderer,
545
AlarmApplet *applet = (AlarmApplet *)data;
546
AlarmListWindow *list_window = applet->list_window;
547
GtkTreeModel *model = GTK_TREE_MODEL (list_window->model);
551
if (gtk_tree_model_get_iter_from_string (model, &iter, path)) {
552
gtk_tree_model_get (model, &iter, COLUMN_ALARM, &a, -1);
554
g_debug ("AlarmListWindow enable toggled on %p", a);
556
// Reset reordered flag
557
list_window->reordered = FALSE;
559
// Select the toggled alarm
560
alarm_list_window_select_alarm (list_window, a);
562
// Let selection_changed know an alarm was just toggled so
563
// this alarm is re-selected if the rows are reordered
564
list_window->toggled = TRUE;
566
// Activate the enabled action
567
gtk_action_activate (GTK_ACTION (applet->action_enabled));
572
* Rows reordered callback
575
alarm_list_window_rows_reordered (GtkTreeModel *model,
581
AlarmApplet *applet = (AlarmApplet *)data;
582
AlarmListWindow *list_window = applet->list_window;
585
// Return if list_window is not set. This happens during initialization.
589
// Retain selected alarm
590
a = alarm_list_window_get_selected_alarm (list_window);
592
// Signal to selection_changed that the rows have been reordered.
593
list_window->reordered = TRUE;
602
* Snooze menu item activated
605
alarm_list_window_snooze_menu_activated (GtkMenuItem *menuitem,
608
AlarmApplet *applet = (AlarmApplet *)data;
613
// g_debug ("AlarmListWindow: snooze-menu activated %s to %d",
614
// gtk_menu_item_get_label (menuitem), gtk_check_menu_item_get_active (menuitem));
616
if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) {
617
// Determine #mins from the name of the menu item (hackish)
618
// Assumes name follows the format {foo}-{mins}
619
parts = g_strsplit (gtk_widget_get_name (GTK_WIDGET (menuitem)), "-", 0);
620
for (i = 0; parts[i] != NULL; i++)
621
// Loop to the last element
624
mins = g_strtod (parts[i-1], NULL);
626
g_debug ("AlarmListWindow: snooze-menu activated: Snooze for %d mins!", mins);
628
applet->snooze_mins = mins;
630
gtk_action_activate (applet->action_snooze);
635
* Snooze menu custom item activated
638
alarm_list_window_snooze_menu_custom_activated (GtkMenuItem *menuitem,
641
AlarmApplet *applet = (AlarmApplet *)data;
642
AlarmListWindow *list_window = applet->list_window;
643
GtkWidget *dialog, *spin;
648
g_debug ("AlarmListWindow: snooze-menu custom activated");
650
dialog = GTK_WIDGET (gtk_builder_get_object (applet->ui, "snooze-dialog"));
651
spin = GTK_WIDGET (gtk_builder_get_object (applet->ui, "snooze-spin"));
653
// Run dialog, hide for later use
654
response = gtk_dialog_run (GTK_DIALOG (dialog));
655
gtk_widget_hide (GTK_WIDGET (dialog));
657
if (response == GTK_RESPONSE_OK) {
658
mins = (gint) gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
659
if ((a = alarm_list_window_get_selected_alarm (list_window))) {
660
g_debug ("AlarmListWindow: Snooze Custom: %s for %d mins", a->message, mins);
661
alarm_snooze (a, mins * 60);
667
* Update the snooze menu according to the applet's snooze_mins property
670
alarm_list_window_snooze_menu_update (AlarmListWindow *list_window)
672
AlarmApplet *applet = (AlarmApplet *)list_window->applet;
673
GtkMenuShell *menu = GTK_MENU_SHELL(list_window->snooze_menu);
674
gchar *target_name = g_strdup_printf ("snooze-menu-%d", applet->snooze_mins);
680
g_debug ("AlarmListWindow: menu_update to %d", applet->snooze_mins);
682
block_list (menu->children, alarm_list_window_snooze_menu_activated);
684
for (l = menu->children; l != NULL; l = l->next) {
685
item = GTK_MENU_ITEM (l->data);
686
name = gtk_widget_get_name (GTK_WIDGET (item));
687
if (g_strcmp0 (name, target_name) == 0) {
688
g_object_set (item, "active", TRUE, NULL);
690
g_debug ("AlarmListWindow: menu_update to %s", name);
694
unblock_list (menu->children, alarm_list_window_snooze_menu_activated);
696
g_free (target_name);