1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 * SECTION: e-reminders-widget
20 * @include: libedataserverui/libedataserverui.h
21 * @short_description: An #ERemindersWidget to work with past reminders
23 * The #ERemindersWidget is a widget which does common tasks on past reminders
24 * provided by #EReminderWatcher. The owner should connect to the "changed" signal
25 * to be notified on any changes, including when the list of past reminders
26 * is either expanded or shrunk, which usually causes the dialog with this
27 * widget to be shown or hidden.
29 * The widget itself is an #EExtensible.
31 * The widget does not listen to #EReminderWatcher::triggered signal.
34 #include "evolution-data-server-config.h"
36 #include <glib/gi18n-lib.h>
38 #include "libedataserver/libedataserver.h"
39 #include "libecal/libecal.h"
41 #include "libedataserverui-private.h"
43 #include "e-reminders-widget.h"
45 #define MAX_CUSTOM_SNOOZE_VALUES 7
47 struct _ERemindersWidgetPrivate
{
48 EReminderWatcher
*watcher
;
52 GtkTreeView
*tree_view
;
53 GtkWidget
*dismiss_button
;
54 GtkWidget
*dismiss_all_button
;
55 GtkWidget
*snooze_combo
;
56 GtkWidget
*snooze_button
;
58 GtkWidget
*add_snooze_popover
;
59 GtkWidget
*add_snooze_days_spin
;
60 GtkWidget
*add_snooze_hours_spin
;
61 GtkWidget
*add_snooze_minutes_spin
;
62 GtkWidget
*add_snooze_add_button
;
66 GCancellable
*cancellable
;
67 guint refresh_idle_id
;
70 guint overdue_update_id
;
71 gint64 last_overdue_update
; /* in seconds */
72 gboolean overdue_update_rounded
;
74 gboolean updating_snooze_combo
;
75 gint last_selected_snooze_minutes
; /* not the same as the saved value in GSettings */
90 static guint signals
[LAST_SIGNAL
];
92 G_DEFINE_TYPE_WITH_CODE (ERemindersWidget
, e_reminders_widget
, GTK_TYPE_GRID
,
93 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE
, NULL
))
96 reminders_widget_snooze_combo_separator_cb (GtkTreeModel
*model
,
105 gtk_tree_model_get (model
, iter
, 1, &minutes
, -1);
111 reminders_widget_new_snooze_combo (void)
114 GtkListStore
*list_store
;
115 GtkCellRenderer
*renderer
;
117 list_store
= gtk_list_store_new (2, G_TYPE_STRING
, G_TYPE_INT
);
119 combo
= gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store
));
121 g_object_unref (list_store
);
123 renderer
= gtk_cell_renderer_text_new ();
124 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo
), renderer
, TRUE
);
125 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo
), renderer
, "text", 0, NULL
);
127 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo
),
128 reminders_widget_snooze_combo_separator_cb
, NULL
, NULL
);
134 reminders_widget_fill_snooze_combo (ERemindersWidget
*reminders
,
135 gint preselect_minutes
)
137 const gint predefined_minutes
[] = {
146 gint ii
, last_sel
= -1;
148 GtkListStore
*list_store
;
149 GtkTreeIter iter
, tosel_iter
;
151 gboolean tosel_set
= FALSE
;
152 gboolean any_stored_added
= FALSE
;
154 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
156 reminders
->priv
->updating_snooze_combo
= TRUE
;
158 combo
= GTK_COMBO_BOX (reminders
->priv
->snooze_combo
);
159 list_store
= GTK_LIST_STORE (gtk_combo_box_get_model (combo
));
161 if (gtk_combo_box_get_active_iter (combo
, &iter
)) {
162 gtk_tree_model_get (GTK_TREE_MODEL (list_store
), &iter
, 1, &last_sel
, -1);
165 gtk_list_store_clear (list_store
);
167 #define add_minutes(_minutes) G_STMT_START { \
168 gint32 minutes = (_minutes); \
171 text = e_cal_util_seconds_to_string (minutes * 60); \
172 gtk_list_store_append (list_store, &iter); \
173 gtk_list_store_set (list_store, &iter, \
179 if (preselect_minutes > 0 && preselect_minutes == minutes) { \
183 } else if (last_sel > 0 && minutes == last_sel) { \
189 /* Custom user values first */
190 variant
= g_settings_get_value (reminders
->priv
->settings
, "notify-custom-snooze-minutes");
192 const gint32
*stored
;
195 stored
= g_variant_get_fixed_array (variant
, &nstored
, sizeof (gint32
));
196 if (stored
&& nstored
> 0) {
197 for (ii
= 0; ii
< nstored
; ii
++) {
198 if (stored
[ii
] > 0) {
199 add_minutes (stored
[ii
]);
200 any_stored_added
= TRUE
;
205 g_variant_unref (variant
);
207 if (any_stored_added
) {
209 gtk_list_store_append (list_store
, &iter
);
210 gtk_list_store_set (list_store
, &iter
, 1, 0, -1);
214 for (ii
= 0; ii
< G_N_ELEMENTS (predefined_minutes
); ii
++) {
215 add_minutes (predefined_minutes
[ii
]);
221 gtk_list_store_append (list_store
, &iter
);
222 gtk_list_store_set (list_store
, &iter
, 1, 0, -1);
224 gtk_list_store_append (list_store
, &iter
);
225 gtk_list_store_set (list_store
, &iter
, 0, _("Add custom timeā¦"), 1, -1, -1);
227 if (any_stored_added
) {
228 gtk_list_store_append (list_store
, &iter
);
229 gtk_list_store_set (list_store
, &iter
, 0, _("Clear custom times"), 1, -2, -1);
232 reminders
->priv
->updating_snooze_combo
= FALSE
;
235 gtk_combo_box_set_active_iter (combo
, &tosel_iter
);
237 gtk_combo_box_set_active (combo
, 0);
241 reminders_widget_custom_snooze_minutes_changed_cb (GSettings
*settings
,
245 ERemindersWidget
*reminders
= user_data
;
247 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
249 reminders_widget_fill_snooze_combo (reminders
, -1);
253 reminders_get_reminder_markups (ERemindersWidget
*reminders
,
254 const EReminderData
*rd
,
255 gchar
**out_overdue_markup
,
256 gchar
**out_description_markup
)
258 g_return_if_fail (rd
!= NULL
);
260 if (out_overdue_markup
) {
265 diff
= (g_get_real_time () / G_USEC_PER_SEC
) - ((gint64
) rd
->instance
.occur_start
);
266 in_future
= diff
< 0;
271 if (in_future
&& (diff
% 60) > 0)
277 time_str
= g_strdup (C_("overdue", "now"));
278 } else if (diff
< 60) {
279 time_str
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "%d minute", "%d minutes", diff
), (gint
) diff
);
280 } else if (diff
< 24 * 60) {
281 gint hours
= diff
/ 60;
283 time_str
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "%d hour", "%d hours", hours
), hours
);
284 } else if (diff
< 7 * 24 * 60) {
285 gint days
= diff
/ (24 * 60);
287 time_str
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "%d day", "%d days", days
), days
);
288 } else if (diff
< 54 * 7 * 24 * 60) {
289 gint weeks
= diff
/ (7 * 24 * 60);
291 time_str
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "%d week", "%d weeks", weeks
), weeks
);
293 gint years
= diff
/ (366 * 24 * 60);
295 time_str
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "%d year", "%d years", years
), years
);
298 if (in_future
|| !diff
) {
299 *out_overdue_markup
= g_markup_printf_escaped ("<span size=\"x-small\">%s</span>", time_str
);
301 *out_overdue_markup
= g_markup_printf_escaped ("<span size=\"x-small\">%s\n%s</span>", time_str
, C_("overdue", "overdue"));
307 if (out_description_markup
) {
308 *out_description_markup
= e_reminder_watcher_describe_data (reminders
->priv
->watcher
, rd
, E_REMINDER_WATCHER_DESCRIBE_FLAG_MARKUP
);
313 reminders_widget_overdue_update (ERemindersWidget
*reminders
)
315 GtkListStore
*list_store
;
318 gboolean any_changed
= FALSE
;
320 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
322 model
= gtk_tree_view_get_model (reminders
->priv
->tree_view
);
326 if (!gtk_tree_model_get_iter_first (model
, &iter
))
329 list_store
= GTK_LIST_STORE (model
);
332 EReminderData
*rd
= NULL
;
334 gtk_tree_model_get (model
, &iter
,
335 E_REMINDERS_WIDGET_COLUMN_REMINDER_DATA
, &rd
,
339 gchar
*overdue_markup
= NULL
;
341 reminders_get_reminder_markups (reminders
, rd
, &overdue_markup
, NULL
);
342 if (overdue_markup
) {
343 gchar
*current
= NULL
;
345 gtk_tree_model_get (model
, &iter
,
346 E_REMINDERS_WIDGET_COLUMN_OVERDUE
, ¤t
,
349 if (g_strcmp0 (current
, overdue_markup
) != 0) {
350 gtk_list_store_set (list_store
, &iter
,
351 E_REMINDERS_WIDGET_COLUMN_OVERDUE
, overdue_markup
,
356 g_free (overdue_markup
);
360 e_reminder_data_free (rd
);
362 } while (gtk_tree_model_iter_next (model
, &iter
));
365 GtkTreeViewColumn
*column
;
367 column
= gtk_tree_view_get_column (reminders
->priv
->tree_view
, 0);
369 gtk_tree_view_column_queue_resize (column
);
374 reminders_widget_overdue_update_cb (gpointer user_data
)
376 ERemindersWidget
*reminders
= user_data
;
377 gint64 now_seconds
, last_update
;
379 if (g_source_is_destroyed (g_main_current_source ()))
382 g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders
), FALSE
);
384 reminders_widget_overdue_update (reminders
);
386 now_seconds
= g_get_real_time () / G_USEC_PER_SEC
;
387 last_update
= reminders
->priv
->last_overdue_update
;
388 reminders
->priv
->last_overdue_update
= now_seconds
;
390 if (!last_update
|| (
391 (now_seconds
- last_update
) % 60 > 2 &&
392 (now_seconds
- last_update
) % 60 < 58)) {
393 gint until_minute
= 60 - (now_seconds
% 60);
395 if (until_minute
>= 59) {
396 reminders
->priv
->overdue_update_rounded
= TRUE
;
399 reminders
->priv
->overdue_update_rounded
= FALSE
;
402 reminders
->priv
->overdue_update_id
= g_timeout_add_seconds (until_minute
,
403 reminders_widget_overdue_update_cb
, reminders
);
406 } else if (!reminders
->priv
->overdue_update_rounded
) {
407 reminders
->priv
->overdue_update_rounded
= TRUE
;
408 reminders
->priv
->overdue_update_id
= g_timeout_add_seconds (60,
409 reminders_widget_overdue_update_cb
, reminders
);
418 reminders_widget_maybe_schedule_overdue_update (ERemindersWidget
*reminders
)
420 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
422 if (reminders
->priv
->is_empty
|| !reminders
->priv
->is_mapped
) {
423 if (reminders
->priv
->overdue_update_id
) {
424 g_source_remove (reminders
->priv
->overdue_update_id
);
425 reminders
->priv
->overdue_update_id
= 0;
427 } else if (!reminders
->priv
->overdue_update_id
) {
428 gint until_minute
= 60 - ((g_get_real_time () / G_USEC_PER_SEC
) % 60);
430 reminders
->priv
->last_overdue_update
= g_get_real_time () / G_USEC_PER_SEC
;
432 if (until_minute
>= 59) {
433 reminders
->priv
->overdue_update_rounded
= TRUE
;
436 reminders
->priv
->overdue_update_rounded
= FALSE
;
439 reminders
->priv
->overdue_update_id
= g_timeout_add_seconds (until_minute
,
440 reminders_widget_overdue_update_cb
, reminders
);
445 reminders_widget_map (GtkWidget
*widget
)
447 ERemindersWidget
*reminders
;
449 g_return_if_fail (E_IS_REMINDERS_WIDGET (widget
));
451 /* Chain up to parent's method. */
452 GTK_WIDGET_CLASS (e_reminders_widget_parent_class
)->map (widget
);
454 reminders
= E_REMINDERS_WIDGET (widget
);
455 reminders
->priv
->is_mapped
= TRUE
;
457 reminders_widget_maybe_schedule_overdue_update (reminders
);
462 reminders_widget_unmap (GtkWidget
*widget
)
464 ERemindersWidget
*reminders
;
466 g_return_if_fail (E_IS_REMINDERS_WIDGET (widget
));
468 /* Chain up to parent's method. */
469 GTK_WIDGET_CLASS (e_reminders_widget_parent_class
)->unmap (widget
);
471 reminders
= E_REMINDERS_WIDGET (widget
);
472 reminders
->priv
->is_mapped
= FALSE
;
474 reminders_widget_maybe_schedule_overdue_update (reminders
);
478 reminders_sort_by_occur (gconstpointer ptr1
,
481 const EReminderData
*rd1
= ptr1
, *rd2
= ptr2
;
485 return rd1
== rd2
? 0 : rd1
? 1 : -1;
487 if (rd1
->instance
.occur_start
!= rd2
->instance
.occur_start
)
488 return rd1
->instance
.occur_start
< rd2
->instance
.occur_start
? -1 : 1;
490 if (rd1
->instance
.trigger
!= rd2
->instance
.trigger
)
491 return rd1
->instance
.trigger
< rd2
->instance
.trigger
? -1 : 1;
493 cmp
= g_strcmp0 (rd1
->source_uid
, rd2
->source_uid
);
495 cmp
= g_strcmp0 (rd1
->instance
.auid
, rd2
->instance
.auid
);
501 reminders_widget_set_is_empty (ERemindersWidget
*reminders
,
504 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
506 if (!is_empty
== !reminders
->priv
->is_empty
)
509 reminders
->priv
->is_empty
= is_empty
;
511 g_object_notify (G_OBJECT (reminders
), "empty");
513 reminders_widget_maybe_schedule_overdue_update (reminders
);
517 reminders_widget_invert_tree_path_compare (gconstpointer ptr1
,
520 return (-1) * gtk_tree_path_compare (ptr1
, ptr2
);
524 reminders_widget_select_one_of (ERemindersWidget
*reminders
,
525 GList
**inout_previous_paths
) /* GtkTreePath * */
532 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
534 if (!inout_previous_paths
|| !*inout_previous_paths
)
537 n_rows
= gtk_tree_model_iter_n_children (gtk_tree_view_get_model (reminders
->priv
->tree_view
), NULL
);
541 *inout_previous_paths
= g_list_sort (*inout_previous_paths
, reminders_widget_invert_tree_path_compare
);
543 len
= g_list_length (*inout_previous_paths
);
545 for (link
= *inout_previous_paths
; link
&& to_select
== -1; link
= g_list_next (link
), len
--) {
546 GtkTreePath
*path
= link
->data
;
547 gint
*indices
, index
;
549 if (!path
|| gtk_tree_path_get_depth (path
) != 1)
552 indices
= gtk_tree_path_get_indices (path
);
556 index
= indices
[0] - len
+ 1;
559 to_select
= n_rows
- 1;
564 if (to_select
>= 0 && to_select
< n_rows
) {
567 path
= gtk_tree_path_new_from_indices (to_select
, -1);
569 gtk_tree_selection_select_path (gtk_tree_view_get_selection (reminders
->priv
->tree_view
), path
);
570 gtk_tree_path_free (path
);
576 reminders_widget_refresh_content_cb (gpointer user_data
)
578 ERemindersWidget
*reminders
= user_data
;
579 GList
*previous_paths
;
582 GtkTreeSelection
*selection
;
583 GtkListStore
*list_store
;
585 if (g_source_is_destroyed (g_main_current_source ()))
588 g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders
), FALSE
);
590 reminders
->priv
->refresh_idle_id
= 0;
592 model
= gtk_tree_view_get_model (reminders
->priv
->tree_view
);
596 selection
= gtk_tree_view_get_selection (reminders
->priv
->tree_view
);
597 previous_paths
= gtk_tree_selection_get_selected_rows (selection
, NULL
);
598 list_store
= GTK_LIST_STORE (model
);
600 g_object_ref (model
);
601 gtk_tree_view_set_model (reminders
->priv
->tree_view
, NULL
);
603 gtk_list_store_clear (list_store
);
605 past
= e_reminder_watcher_dup_past (reminders
->priv
->watcher
);
610 past
= g_slist_sort (past
, reminders_sort_by_occur
);
611 for (link
= past
; link
; link
= g_slist_next (link
)) {
612 const EReminderData
*rd
= link
->data
;
613 gchar
*overdue
= NULL
, *description
= NULL
;
615 if (!rd
|| !rd
->component
)
618 reminders_get_reminder_markups (reminders
, rd
, &overdue
, &description
);
620 gtk_list_store_append (list_store
, &iter
);
621 gtk_list_store_set (list_store
, &iter
,
622 E_REMINDERS_WIDGET_COLUMN_OVERDUE
, overdue
,
623 E_REMINDERS_WIDGET_COLUMN_DESCRIPTION
, description
,
624 E_REMINDERS_WIDGET_COLUMN_REMINDER_DATA
, rd
,
627 g_free (description
);
632 gtk_tree_view_set_model (reminders
->priv
->tree_view
, model
);
633 g_object_unref (model
);
635 reminders_widget_set_is_empty (reminders
, !past
);
638 GtkTreeViewColumn
*column
;
640 column
= gtk_tree_view_get_column (reminders
->priv
->tree_view
, 0);
642 gtk_tree_view_column_queue_resize (column
);
644 reminders_widget_select_one_of (reminders
, &previous_paths
);
647 g_list_free_full (previous_paths
, (GDestroyNotify
) gtk_tree_path_free
);
648 g_slist_free_full (past
, e_reminder_data_free
);
650 g_signal_emit (reminders
, signals
[CHANGED
], 0, NULL
);
656 reminders_widget_schedule_content_refresh (ERemindersWidget
*reminders
)
658 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
660 if (!reminders
->priv
->refresh_idle_id
) {
661 reminders
->priv
->refresh_idle_id
= g_idle_add_full (G_PRIORITY_DEFAULT_IDLE
,
662 reminders_widget_refresh_content_cb
, reminders
, NULL
);
667 reminders_widget_watcher_changed_cb (EReminderWatcher
*watcher
,
670 ERemindersWidget
*reminders
= user_data
;
672 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
674 reminders_widget_schedule_content_refresh (reminders
);
678 reminders_widget_gather_selected_cb (GtkTreeModel
*model
,
683 GSList
**inout_selected
= user_data
;
684 EReminderData
*rd
= NULL
;
686 g_return_if_fail (inout_selected
!= NULL
);
688 gtk_tree_model_get (model
, iter
, E_REMINDERS_WIDGET_COLUMN_REMINDER_DATA
, &rd
, -1);
691 *inout_selected
= g_slist_prepend (*inout_selected
, rd
);
695 reminders_widget_do_dismiss_cb (ERemindersWidget
*reminders
,
696 const EReminderData
*rd
,
697 GString
*gathered_errors
,
698 GCancellable
*cancellable
,
701 GError
*local_error
= NULL
;
703 if (g_cancellable_is_cancelled (cancellable
))
706 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
707 g_return_if_fail (rd
!= NULL
);
709 if (!e_reminder_watcher_dismiss_sync (reminders
->priv
->watcher
, rd
, cancellable
, &local_error
) && local_error
&& gathered_errors
&&
710 !g_error_matches (local_error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
711 if (gathered_errors
->len
)
712 g_string_append_c (gathered_errors
, '\n');
713 g_string_append (gathered_errors
, local_error
->message
);
716 g_clear_error (&local_error
);
719 typedef void (* ForeachSelectedSyncFunc
) (ERemindersWidget
*reminders
,
720 const EReminderData
*rd
,
721 GString
*gathered_errors
,
722 GCancellable
*cancellable
,
725 typedef struct _ForeachSelectedData
{
726 GSList
*selected
; /* EReminderData * */
727 ForeachSelectedSyncFunc sync_func
;
729 GDestroyNotify user_data_destroy
;
731 } ForeachSelectedData
;
734 foreach_selected_data_free (gpointer ptr
)
736 ForeachSelectedData
*fsd
= ptr
;
739 g_slist_free_full (fsd
->selected
, e_reminder_data_free
);
740 if (fsd
->user_data_destroy
)
741 fsd
->user_data_destroy (fsd
->user_data
);
742 g_free (fsd
->error_prefix
);
748 reminders_widget_foreach_selected_thread (GTask
*task
,
749 gpointer source_object
,
751 GCancellable
*cancellable
)
753 ForeachSelectedData
*fsd
= task_data
;
754 GString
*gathered_errors
;
757 g_return_if_fail (fsd
!= NULL
);
758 g_return_if_fail (fsd
->selected
!= NULL
);
759 g_return_if_fail (fsd
->sync_func
!= NULL
);
761 if (g_cancellable_is_cancelled (cancellable
))
764 gathered_errors
= g_string_new ("");
766 for (link
= fsd
->selected
; link
&& !g_cancellable_is_cancelled (cancellable
); link
= g_slist_next (link
)) {
767 const EReminderData
*rd
= link
->data
;
769 fsd
->sync_func (source_object
, rd
, gathered_errors
, cancellable
, fsd
->user_data
);
772 if (gathered_errors
->len
) {
773 if (fsd
->error_prefix
) {
774 g_string_prepend_c (gathered_errors
, '\n');
775 g_string_prepend (gathered_errors
, fsd
->error_prefix
);
778 g_task_return_new_error (task
, G_IO_ERROR
, G_IO_ERROR_FAILED
, "%s", gathered_errors
->str
);
780 g_task_return_boolean (task
, TRUE
);
783 g_string_free (gathered_errors
, TRUE
);
787 reminders_widget_foreach_selected_done_cb (GObject
*source_object
,
788 GAsyncResult
*result
,
791 ERemindersWidget
*reminders
;
792 GError
*local_error
= NULL
;
794 g_return_if_fail (E_IS_REMINDERS_WIDGET (source_object
));
796 reminders
= E_REMINDERS_WIDGET (source_object
);
797 g_return_if_fail (g_task_is_valid (result
, reminders
));
799 if (!g_task_propagate_boolean (G_TASK (result
), &local_error
) && local_error
) {
800 e_reminders_widget_report_error (reminders
, NULL
, local_error
);
803 g_clear_error (&local_error
);
807 reminders_widget_foreach_selected (ERemindersWidget
*reminders
,
808 ForeachSelectedSyncFunc sync_func
,
810 GDestroyNotify user_data_destroy
,
811 const gchar
*error_prefix
)
813 GtkTreeSelection
*selection
;
814 GSList
*selected
= NULL
; /* EReminderData * */
817 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
818 g_return_if_fail (sync_func
!= NULL
);
820 selection
= gtk_tree_view_get_selection (reminders
->priv
->tree_view
);
821 gtk_tree_selection_selected_foreach (selection
, reminders_widget_gather_selected_cb
, &selected
);
824 ForeachSelectedData
*fsd
;
826 fsd
= g_new0 (ForeachSelectedData
, 1);
827 fsd
->selected
= selected
; /* Takes ownership */
828 fsd
->sync_func
= sync_func
;
829 fsd
->user_data
= user_data
;
830 fsd
->user_data_destroy
= user_data_destroy
;
831 fsd
->error_prefix
= g_strdup (error_prefix
);
833 task
= g_task_new (reminders
, reminders
->priv
->cancellable
, reminders_widget_foreach_selected_done_cb
, NULL
);
834 g_task_set_task_data (task
, fsd
, foreach_selected_data_free
);
835 g_task_set_check_cancellable (task
, FALSE
);
836 g_task_run_in_thread (task
, reminders_widget_foreach_selected_thread
);
837 g_object_unref (task
);
842 reminders_widget_row_activated_cb (GtkTreeView
*tree_view
,
844 GtkTreeViewColumn
*column
,
847 ERemindersWidget
*reminders
= user_data
;
851 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
856 model
= gtk_tree_view_get_model (reminders
->priv
->tree_view
);
857 if (gtk_tree_model_get_iter (model
, &iter
, path
)) {
858 EReminderData
*rd
= NULL
;
860 gtk_tree_model_get (model
, &iter
,
861 E_REMINDERS_WIDGET_COLUMN_REMINDER_DATA
, &rd
,
865 gboolean result
= FALSE
;
867 g_signal_emit (reminders
, signals
[ACTIVATED
], 0, rd
, &result
);
870 const gchar
*scheme
= NULL
;
871 const gchar
*comp_uid
= NULL
;
873 e_cal_component_get_uid (rd
->component
, &comp_uid
);
875 switch (e_cal_component_get_vtype (rd
->component
)) {
876 case E_CAL_COMPONENT_EVENT
:
877 scheme
= "calendar:";
879 case E_CAL_COMPONENT_TODO
:
882 case E_CAL_COMPONENT_JOURNAL
:
889 if (scheme
&& comp_uid
&& rd
->source_uid
) {
892 GError
*error
= NULL
;
894 uri
= g_string_sized_new (128);
895 g_string_append (uri
, scheme
);
896 g_string_append (uri
, "///?");
898 tmp
= g_uri_escape_string (rd
->source_uid
, NULL
, TRUE
);
899 g_string_append (uri
, "source-uid=");
900 g_string_append (uri
, tmp
);
903 g_string_append (uri
, "&");
905 tmp
= g_uri_escape_string (comp_uid
, NULL
, TRUE
);
906 g_string_append (uri
, "comp-uid=");
907 g_string_append (uri
, tmp
);
910 if (!g_app_info_launch_default_for_uri (uri
->str
, NULL
, &error
) &&
911 !g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_NOT_SUPPORTED
)) {
912 gchar
*prefix
= g_strdup_printf (_("Failed to launch URI ā%sā:"), uri
->str
);
913 e_reminders_widget_report_error (reminders
, prefix
, error
);
917 g_string_free (uri
, TRUE
);
918 g_clear_error (&error
);
922 e_reminder_data_free (rd
);
928 reminders_widget_selection_changed_cb (GtkTreeSelection
*selection
,
931 ERemindersWidget
*reminders
= user_data
;
934 g_return_if_fail (GTK_IS_TREE_SELECTION (selection
));
935 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
937 nselected
= gtk_tree_selection_count_selected_rows (selection
);
938 gtk_widget_set_sensitive (reminders
->priv
->snooze_combo
, nselected
> 0);
939 gtk_widget_set_sensitive (reminders
->priv
->snooze_button
, nselected
> 0);
940 gtk_widget_set_sensitive (reminders
->priv
->dismiss_button
, nselected
> 0);
944 reminders_widget_dismiss_button_clicked_cb (GtkButton
*button
,
947 ERemindersWidget
*reminders
= user_data
;
949 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
951 g_signal_handlers_block_by_func (reminders
->priv
->watcher
, reminders_widget_watcher_changed_cb
, reminders
);
953 reminders_widget_foreach_selected (reminders
, reminders_widget_do_dismiss_cb
, NULL
, NULL
, _("Failed to dismiss reminder:"));
955 g_signal_handlers_unblock_by_func (reminders
->priv
->watcher
, reminders_widget_watcher_changed_cb
, reminders
);
957 reminders_widget_watcher_changed_cb (reminders
->priv
->watcher
, reminders
);
961 reminders_widget_dismiss_all_done_cb (GObject
*source_object
,
962 GAsyncResult
*result
,
965 ERemindersWidget
*reminders
= user_data
;
966 GError
*local_error
= NULL
;
968 g_return_if_fail (E_IS_REMINDER_WATCHER (source_object
));
970 if (!e_reminder_watcher_dismiss_all_finish (reminders
->priv
->watcher
, result
, &local_error
) &&
971 !g_error_matches (local_error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
972 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
974 e_reminders_widget_report_error (reminders
, _("Failed to dismiss all:"), local_error
);
977 g_clear_error (&local_error
);
981 reminders_widget_dismiss_all_button_clicked_cb (GtkButton
*button
,
984 ERemindersWidget
*reminders
= user_data
;
986 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
988 e_reminder_watcher_dismiss_all (reminders
->priv
->watcher
, reminders
->priv
->cancellable
,
989 reminders_widget_dismiss_all_done_cb
, reminders
);
993 reminders_widget_add_snooze_add_button_clicked_cb (GtkButton
*button
,
996 ERemindersWidget
*reminders
= user_data
;
999 gboolean found
= FALSE
;
1002 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1005 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_minutes_spin
)) +
1006 (60 * gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_hours_spin
))) +
1007 (24 * 60 * gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_days_spin
)));
1008 g_return_if_fail (new_minutes
> 0);
1010 gtk_widget_hide (reminders
->priv
->add_snooze_popover
);
1012 model
= gtk_combo_box_get_model (GTK_COMBO_BOX (reminders
->priv
->snooze_combo
));
1013 g_return_if_fail (model
!= NULL
);
1015 if (gtk_tree_model_get_iter_first (model
, &iter
)) {
1019 gtk_tree_model_get (model
, &iter
, 1, &minutes
, -1);
1021 if (minutes
== new_minutes
) {
1023 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (reminders
->priv
->snooze_combo
), &iter
);
1026 } while (gtk_tree_model_iter_next (model
, &iter
));
1031 gint32 array
[MAX_CUSTOM_SNOOZE_VALUES
] = { 0 }, narray
= 0, ii
;
1033 variant
= g_settings_get_value (reminders
->priv
->settings
, "notify-custom-snooze-minutes");
1035 const gint32
*stored
;
1038 stored
= g_variant_get_fixed_array (variant
, &nstored
, sizeof (gint32
));
1039 if (stored
&& nstored
> 0) {
1040 /* Skip the oldest, when too many stored */
1041 for (ii
= nstored
>= MAX_CUSTOM_SNOOZE_VALUES
? 1 : 0; ii
< MAX_CUSTOM_SNOOZE_VALUES
&& ii
< nstored
; ii
++) {
1042 array
[narray
] = stored
[ii
];
1047 g_variant_unref (variant
);
1050 /* Add the new at the end of the array */
1051 array
[narray
] = new_minutes
;
1054 variant
= g_variant_new_fixed_array (G_VARIANT_TYPE_INT32
, array
, narray
, sizeof (gint32
));
1055 g_settings_set_value (reminders
->priv
->settings
, "notify-custom-snooze-minutes", variant
);
1057 reminders_widget_fill_snooze_combo (reminders
, new_minutes
);
1062 reminders_widget_add_snooze_update_sensitize_cb (GtkSpinButton
*spin
,
1065 ERemindersWidget
*reminders
= user_data
;
1067 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1069 gtk_widget_set_sensitive (reminders
->priv
->add_snooze_add_button
,
1070 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_minutes_spin
)) +
1071 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_hours_spin
)) +
1072 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_days_spin
)) > 0);
1076 reminders_widget_snooze_add_custom (ERemindersWidget
*reminders
)
1080 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1082 if (!reminders
->priv
->add_snooze_popover
) {
1086 reminders
->priv
->add_snooze_days_spin
= gtk_spin_button_new_with_range (0.0, 366.0, 1.0);
1087 reminders
->priv
->add_snooze_hours_spin
= gtk_spin_button_new_with_range (0.0, 23.0, 1.0);
1088 reminders
->priv
->add_snooze_minutes_spin
= gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
1090 g_object_set (G_OBJECT (reminders
->priv
->add_snooze_days_spin
),
1093 "snap-to-ticks", TRUE
,
1096 g_object_set (G_OBJECT (reminders
->priv
->add_snooze_hours_spin
),
1099 "snap-to-ticks", TRUE
,
1102 g_object_set (G_OBJECT (reminders
->priv
->add_snooze_minutes_spin
),
1105 "snap-to-ticks", TRUE
,
1108 vbox
= GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL
, 2));
1110 widget
= gtk_label_new (_("Set a custom snooze time for"));
1111 gtk_box_pack_start (vbox
, widget
, FALSE
, FALSE
, 0);
1113 box
= GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 2));
1114 g_object_set (G_OBJECT (box
),
1115 "halign", GTK_ALIGN_START
,
1117 "valign", GTK_ALIGN_CENTER
,
1121 gtk_box_pack_start (box
, reminders
->priv
->add_snooze_days_spin
, FALSE
, FALSE
, 4);
1122 /* Translators: this is part of: "Set a custom snooze time for [nnn] days [nnn] hours [nnn] minutes", where the text in "[]" means a separate widget */
1123 widget
= gtk_label_new_with_mnemonic (C_("reminders-snooze", "da_ys"));
1124 gtk_label_set_mnemonic_widget (GTK_LABEL (widget
), reminders
->priv
->add_snooze_days_spin
);
1125 gtk_box_pack_start (box
, widget
, FALSE
, FALSE
, 4);
1127 gtk_box_pack_start (vbox
, GTK_WIDGET (box
), FALSE
, FALSE
, 0);
1129 box
= GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 2));
1130 g_object_set (G_OBJECT (box
),
1131 "halign", GTK_ALIGN_START
,
1133 "valign", GTK_ALIGN_CENTER
,
1137 gtk_box_pack_start (box
, reminders
->priv
->add_snooze_hours_spin
, FALSE
, FALSE
, 4);
1138 /* Translators: this is part of: "Set a custom snooze time for [nnn] days [nnn] hours [nnn] minutes", where the text in "[]" means a separate widget */
1139 widget
= gtk_label_new_with_mnemonic (C_("reminders-snooze", "_hours"));
1140 gtk_label_set_mnemonic_widget (GTK_LABEL (widget
), reminders
->priv
->add_snooze_hours_spin
);
1141 gtk_box_pack_start (box
, widget
, FALSE
, FALSE
, 4);
1143 gtk_box_pack_start (vbox
, GTK_WIDGET (box
), FALSE
, FALSE
, 0);
1145 box
= GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 2));
1146 g_object_set (G_OBJECT (box
),
1147 "halign", GTK_ALIGN_START
,
1149 "valign", GTK_ALIGN_CENTER
,
1153 gtk_box_pack_start (box
, reminders
->priv
->add_snooze_minutes_spin
, FALSE
, FALSE
, 4);
1154 /* Translators: this is part of: "Set a custom snooze time for [nnn] days [nnn] hours [nnn] minutes", where the text in "[]" means a separate widget */
1155 widget
= gtk_label_new_with_mnemonic (C_("reminders-snooze", "_minutes"));
1156 gtk_label_set_mnemonic_widget (GTK_LABEL (widget
), reminders
->priv
->add_snooze_minutes_spin
);
1157 gtk_box_pack_start (box
, widget
, FALSE
, FALSE
, 4);
1159 gtk_box_pack_start (vbox
, GTK_WIDGET (box
), FALSE
, FALSE
, 0);
1161 reminders
->priv
->add_snooze_add_button
= gtk_button_new_with_mnemonic (_("_Add Snooze time"));
1162 g_object_set (G_OBJECT (reminders
->priv
->add_snooze_add_button
),
1163 "halign", GTK_ALIGN_CENTER
,
1166 gtk_box_pack_start (vbox
, reminders
->priv
->add_snooze_add_button
, FALSE
, FALSE
, 0);
1168 gtk_widget_show_all (GTK_WIDGET (vbox
));
1170 reminders
->priv
->add_snooze_popover
= gtk_popover_new (GTK_WIDGET (reminders
));
1171 gtk_popover_set_position (GTK_POPOVER (reminders
->priv
->add_snooze_popover
), GTK_POS_BOTTOM
);
1172 gtk_container_add (GTK_CONTAINER (reminders
->priv
->add_snooze_popover
), GTK_WIDGET (vbox
));
1173 gtk_container_set_border_width (GTK_CONTAINER (reminders
->priv
->add_snooze_popover
), 6);
1175 g_signal_connect (reminders
->priv
->add_snooze_add_button
, "clicked",
1176 G_CALLBACK (reminders_widget_add_snooze_add_button_clicked_cb
), reminders
);
1178 g_signal_connect (reminders
->priv
->add_snooze_days_spin
, "value-changed",
1179 G_CALLBACK (reminders_widget_add_snooze_update_sensitize_cb
), reminders
);
1181 g_signal_connect (reminders
->priv
->add_snooze_hours_spin
, "value-changed",
1182 G_CALLBACK (reminders_widget_add_snooze_update_sensitize_cb
), reminders
);
1184 g_signal_connect (reminders
->priv
->add_snooze_minutes_spin
, "value-changed",
1185 G_CALLBACK (reminders_widget_add_snooze_update_sensitize_cb
), reminders
);
1187 reminders_widget_add_snooze_update_sensitize_cb (NULL
, reminders
);
1190 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (reminders
->priv
->snooze_combo
), &iter
)) {
1193 gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (reminders
->priv
->snooze_combo
)), &iter
, 1, &minutes
, -1);
1196 gtk_spin_button_set_value (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_minutes_spin
), minutes
% 60);
1198 minutes
= minutes
/ 60;
1199 gtk_spin_button_set_value (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_hours_spin
), minutes
% 24);
1201 minutes
= minutes
/ 24;
1202 gtk_spin_button_set_value (GTK_SPIN_BUTTON (reminders
->priv
->add_snooze_days_spin
), minutes
);
1206 gtk_widget_hide (reminders
->priv
->add_snooze_popover
);
1207 gtk_popover_set_relative_to (GTK_POPOVER (reminders
->priv
->add_snooze_popover
), reminders
->priv
->snooze_combo
);
1208 gtk_widget_show (reminders
->priv
->add_snooze_popover
);
1210 gtk_widget_grab_focus (reminders
->priv
->add_snooze_days_spin
);
1214 reminders_widget_snooze_combo_changed_cb (GtkComboBox
*combo
,
1217 ERemindersWidget
*reminders
= user_data
;
1220 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1222 if (reminders
->priv
->updating_snooze_combo
)
1225 if (gtk_combo_box_get_active_iter (combo
, &iter
)) {
1226 GtkTreeModel
*model
;
1229 model
= gtk_combo_box_get_model (combo
);
1231 gtk_tree_model_get (model
, &iter
, 1, &minutes
, -1);
1234 reminders
->priv
->last_selected_snooze_minutes
= minutes
;
1235 } else if (minutes
== -1 || minutes
== -2) {
1236 if (reminders
->priv
->last_selected_snooze_minutes
) {
1237 reminders
->priv
->updating_snooze_combo
= TRUE
;
1239 if (gtk_tree_model_get_iter_first (model
, &iter
)) {
1243 gtk_tree_model_get (model
, &iter
, 1, &stored
, -1);
1244 if (stored
== reminders
->priv
->last_selected_snooze_minutes
) {
1245 gtk_combo_box_set_active_iter (combo
, &iter
);
1248 } while (gtk_tree_model_iter_next (model
, &iter
));
1251 reminders
->priv
->updating_snooze_combo
= FALSE
;
1254 /* The "Add custom" item was selected */
1255 if (minutes
== -1) {
1256 reminders_widget_snooze_add_custom (reminders
);
1257 /* The "Clear custom times" item was selected */
1258 } else if (minutes
== -2) {
1259 g_settings_reset (reminders
->priv
->settings
, "notify-custom-snooze-minutes");
1266 reminders_widget_snooze_button_clicked_cb (GtkButton
*button
,
1269 ERemindersWidget
*reminders
= user_data
;
1270 GtkTreeSelection
*selection
;
1271 GSList
*selected
= NULL
, *link
;
1276 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1277 g_return_if_fail (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (reminders
->priv
->snooze_combo
), &iter
));
1279 gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (reminders
->priv
->snooze_combo
)), &iter
, 1, &minutes
, -1);
1281 g_return_if_fail (minutes
> 0);
1283 until
= (g_get_real_time () / G_USEC_PER_SEC
) + (minutes
* 60);
1285 g_settings_set_int (reminders
->priv
->settings
, "notify-last-snooze-minutes", minutes
);
1287 selection
= gtk_tree_view_get_selection (reminders
->priv
->tree_view
);
1288 gtk_tree_selection_selected_foreach (selection
, reminders_widget_gather_selected_cb
, &selected
);
1290 g_signal_handlers_block_by_func (reminders
->priv
->watcher
, reminders_widget_watcher_changed_cb
, reminders
);
1292 for (link
= selected
; link
; link
= g_slist_next (link
)) {
1293 const EReminderData
*rd
= link
->data
;
1295 e_reminder_watcher_snooze (reminders
->priv
->watcher
, rd
, until
);
1298 g_slist_free_full (selected
, e_reminder_data_free
);
1300 g_signal_handlers_unblock_by_func (reminders
->priv
->watcher
, reminders_widget_watcher_changed_cb
, reminders
);
1303 reminders_widget_watcher_changed_cb (reminders
->priv
->watcher
, reminders
);
1307 reminders_widget_set_watcher (ERemindersWidget
*reminders
,
1308 EReminderWatcher
*watcher
)
1310 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1311 g_return_if_fail (E_IS_REMINDER_WATCHER (watcher
));
1312 g_return_if_fail (reminders
->priv
->watcher
== NULL
);
1314 reminders
->priv
->watcher
= g_object_ref (watcher
);
1318 reminders_widget_set_property (GObject
*object
,
1320 const GValue
*value
,
1323 switch (property_id
) {
1325 reminders_widget_set_watcher (
1326 E_REMINDERS_WIDGET (object
),
1327 g_value_get_object (value
));
1331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1335 reminders_widget_get_property (GObject
*object
,
1340 switch (property_id
) {
1342 g_value_set_object (
1343 value
, e_reminders_widget_get_watcher (
1344 E_REMINDERS_WIDGET (object
)));
1348 g_value_set_boolean (
1349 value
, e_reminders_widget_is_empty (
1350 E_REMINDERS_WIDGET (object
)));
1354 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1358 reminders_widget_constructed (GObject
*object
)
1360 ERemindersWidget
*reminders
= E_REMINDERS_WIDGET (object
);
1361 GtkWidget
*scrolled_window
;
1362 GtkListStore
*list_store
;
1363 GtkTreeSelection
*selection
;
1364 GtkTreeViewColumn
*column
;
1365 GtkCellRenderer
*renderer
;
1369 /* Chain up to parent's method. */
1370 G_OBJECT_CLASS (e_reminders_widget_parent_class
)->constructed (object
);
1372 scrolled_window
= gtk_scrolled_window_new (NULL
, NULL
);
1373 g_object_set (G_OBJECT (scrolled_window
),
1374 "halign", GTK_ALIGN_FILL
,
1376 "valign", GTK_ALIGN_FILL
,
1378 "hscrollbar-policy", GTK_POLICY_NEVER
,
1379 "vscrollbar-policy", GTK_POLICY_AUTOMATIC
,
1380 "shadow-type", GTK_SHADOW_IN
,
1383 gtk_grid_attach (GTK_GRID (reminders
), scrolled_window
, 0, 0, 1, 1);
1385 list_store
= gtk_list_store_new (E_REMINDERS_WIDGET_N_COLUMNS
,
1386 G_TYPE_STRING
, /* E_REMINDERS_WIDGET_COLUMN_OVERDUE */
1387 G_TYPE_STRING
, /* E_REMINDERS_WIDGET_COLUMN_DESCRIPTION */
1388 E_TYPE_REMINDER_DATA
); /* E_REMINDERS_WIDGET_COLUMN_REMINDER_DATA */
1390 reminders
->priv
->tree_view
= GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store
)));
1392 g_object_unref (list_store
);
1394 g_object_set (G_OBJECT (reminders
->priv
->tree_view
),
1395 "halign", GTK_ALIGN_FILL
,
1397 "valign", GTK_ALIGN_FILL
,
1399 "activate-on-single-click", FALSE
,
1400 "enable-search", FALSE
,
1401 "fixed-height-mode", TRUE
,
1402 "headers-visible", FALSE
,
1403 "hover-selection", FALSE
,
1406 gtk_container_add (GTK_CONTAINER (scrolled_window
), GTK_WIDGET (reminders
->priv
->tree_view
));
1408 gtk_tree_view_set_tooltip_column (reminders
->priv
->tree_view
, E_REMINDERS_WIDGET_COLUMN_DESCRIPTION
);
1410 /* Headers not visible, thus column's caption is not localized */
1411 gtk_tree_view_insert_column_with_attributes (reminders
->priv
->tree_view
, -1, "Overdue",
1412 gtk_cell_renderer_text_new (), "markup", E_REMINDERS_WIDGET_COLUMN_OVERDUE
, NULL
);
1414 renderer
= gtk_cell_renderer_text_new ();
1415 g_object_set (G_OBJECT (renderer
),
1416 "ellipsize", PANGO_ELLIPSIZE_END
,
1419 gtk_tree_view_insert_column_with_attributes (reminders
->priv
->tree_view
, -1, "Description",
1420 renderer
, "markup", E_REMINDERS_WIDGET_COLUMN_DESCRIPTION
, NULL
);
1422 column
= gtk_tree_view_get_column (reminders
->priv
->tree_view
, 0);
1423 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
1425 column
= gtk_tree_view_get_column (reminders
->priv
->tree_view
, 1);
1426 gtk_tree_view_column_set_expand (column
, TRUE
);
1428 reminders
->priv
->dismiss_button
= gtk_button_new_with_mnemonic (_("_Dismiss"));
1429 reminders
->priv
->dismiss_all_button
= gtk_button_new_with_mnemonic (_("Dismiss _All"));
1430 reminders
->priv
->snooze_combo
= reminders_widget_new_snooze_combo ();
1431 reminders
->priv
->snooze_button
= gtk_button_new_with_mnemonic (_("_Snooze"));
1433 reminders_widget_fill_snooze_combo (reminders
,
1434 g_settings_get_int (reminders
->priv
->settings
, "notify-last-snooze-minutes"));
1436 box
= GTK_BOX (gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL
));
1437 g_object_set (G_OBJECT (box
),
1438 "halign", GTK_ALIGN_END
,
1440 "valign", GTK_ALIGN_CENTER
,
1445 widget
= gtk_label_new ("");
1447 gtk_box_pack_start (box
, reminders
->priv
->snooze_combo
, FALSE
, FALSE
, 0);
1448 gtk_box_pack_start (box
, reminders
->priv
->snooze_button
, FALSE
, FALSE
, 0);
1449 gtk_box_pack_start (box
, widget
, FALSE
, FALSE
, 0);
1450 gtk_box_pack_start (box
, reminders
->priv
->dismiss_button
, FALSE
, FALSE
, 0);
1451 gtk_box_pack_start (box
, reminders
->priv
->dismiss_all_button
, FALSE
, FALSE
, 0);
1453 gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (box
), reminders
->priv
->snooze_combo
, TRUE
);
1454 gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (box
), widget
, TRUE
);
1456 gtk_grid_attach (GTK_GRID (reminders
), GTK_WIDGET (box
), 0, 1, 1, 1);
1458 gtk_widget_show_all (GTK_WIDGET (reminders
));
1460 selection
= gtk_tree_view_get_selection (reminders
->priv
->tree_view
);
1461 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_MULTIPLE
);
1463 g_signal_connect (reminders
->priv
->tree_view
, "row-activated",
1464 G_CALLBACK (reminders_widget_row_activated_cb
), reminders
);
1466 g_signal_connect (selection
, "changed",
1467 G_CALLBACK (reminders_widget_selection_changed_cb
), reminders
);
1469 g_signal_connect (reminders
->priv
->snooze_button
, "clicked",
1470 G_CALLBACK (reminders_widget_snooze_button_clicked_cb
), reminders
);
1472 g_signal_connect (reminders
->priv
->dismiss_button
, "clicked",
1473 G_CALLBACK (reminders_widget_dismiss_button_clicked_cb
), reminders
);
1475 g_signal_connect (reminders
->priv
->dismiss_all_button
, "clicked",
1476 G_CALLBACK (reminders_widget_dismiss_all_button_clicked_cb
), reminders
);
1478 g_signal_connect (reminders
->priv
->watcher
, "changed",
1479 G_CALLBACK (reminders_widget_watcher_changed_cb
), reminders
);
1481 g_signal_connect (reminders
->priv
->snooze_combo
, "changed",
1482 G_CALLBACK (reminders_widget_snooze_combo_changed_cb
), reminders
);
1484 g_signal_connect (reminders
->priv
->settings
, "changed::notify-custom-snooze-minutes",
1485 G_CALLBACK (reminders_widget_custom_snooze_minutes_changed_cb
), reminders
);
1487 e_binding_bind_property (reminders
, "empty",
1488 reminders
->priv
->dismiss_all_button
, "sensitive",
1489 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
1491 e_binding_bind_property (reminders
, "empty",
1492 scrolled_window
, "sensitive",
1493 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
1495 _libedataserverui_load_modules ();
1497 e_extensible_load_extensions (E_EXTENSIBLE (object
));
1499 reminders_widget_schedule_content_refresh (reminders
);
1503 reminders_widget_dispose (GObject
*object
)
1505 ERemindersWidget
*reminders
= E_REMINDERS_WIDGET (object
);
1507 g_cancellable_cancel (reminders
->priv
->cancellable
);
1509 if (reminders
->priv
->refresh_idle_id
) {
1510 g_source_remove (reminders
->priv
->refresh_idle_id
);
1511 reminders
->priv
->refresh_idle_id
= 0;
1514 if (reminders
->priv
->overdue_update_id
) {
1515 g_source_remove (reminders
->priv
->overdue_update_id
);
1516 reminders
->priv
->overdue_update_id
= 0;
1519 if (reminders
->priv
->watcher
)
1520 g_signal_handlers_disconnect_by_data (reminders
->priv
->watcher
, reminders
);
1522 if (reminders
->priv
->settings
)
1523 g_signal_handlers_disconnect_by_data (reminders
->priv
->settings
, reminders
);
1525 /* Chain up to parent's method. */
1526 G_OBJECT_CLASS (e_reminders_widget_parent_class
)->dispose (object
);
1530 reminders_widget_finalize (GObject
*object
)
1532 ERemindersWidget
*reminders
= E_REMINDERS_WIDGET (object
);
1534 g_clear_object (&reminders
->priv
->watcher
);
1535 g_clear_object (&reminders
->priv
->settings
);
1536 g_clear_object (&reminders
->priv
->cancellable
);
1538 /* Chain up to parent's method. */
1539 G_OBJECT_CLASS (e_reminders_widget_parent_class
)->finalize (object
);
1543 e_reminders_widget_class_init (ERemindersWidgetClass
*klass
)
1545 GObjectClass
*object_class
;
1546 GtkWidgetClass
*widget_class
;
1548 g_type_class_add_private (klass
, sizeof (ERemindersWidgetPrivate
));
1550 object_class
= G_OBJECT_CLASS (klass
);
1551 object_class
->set_property
= reminders_widget_set_property
;
1552 object_class
->get_property
= reminders_widget_get_property
;
1553 object_class
->constructed
= reminders_widget_constructed
;
1554 object_class
->dispose
= reminders_widget_dispose
;
1555 object_class
->finalize
= reminders_widget_finalize
;
1557 widget_class
= GTK_WIDGET_CLASS (klass
);
1558 widget_class
->map
= reminders_widget_map
;
1559 widget_class
->unmap
= reminders_widget_unmap
;
1562 * ERemindersWidget::watcher:
1564 * An #EReminderWatcher used to work with reminders.
1568 g_object_class_install_property (
1571 g_param_spec_object (
1574 "The reminder watcher used to work with reminders",
1575 E_TYPE_REMINDER_WATCHER
,
1577 G_PARAM_CONSTRUCT_ONLY
|
1578 G_PARAM_STATIC_STRINGS
));
1581 * ERemindersWidget::empty:
1583 * Set to %TRUE when there's no past reminder in the widget.
1587 g_object_class_install_property (
1590 g_param_spec_boolean (
1593 "Whether there are no past reminders",
1596 G_PARAM_STATIC_STRINGS
));
1599 * ERemindersWidget:changed:
1600 * @reminders: an #ERemindersWidget
1602 * A signal being called to notify about changes in the past reminders list.
1606 signals
[CHANGED
] = g_signal_new (
1608 G_OBJECT_CLASS_TYPE (klass
),
1609 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
1610 G_STRUCT_OFFSET (ERemindersWidgetClass
, changed
),
1613 g_cclosure_marshal_generic
,
1618 * ERemindersWidget:activated:
1619 * @reminders: an #ERemindersWidget
1620 * @rd: an #EReminderData
1622 * A signal being called when the user activates one of the past reminders in the tree view.
1623 * The @rd corresponds to the activated reminder.
1625 * Returns: %TRUE, when the further processing of this signal should be stopped, %FALSE otherwise.
1629 signals
[ACTIVATED
] = g_signal_new (
1631 G_OBJECT_CLASS_TYPE (klass
),
1632 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
1633 G_STRUCT_OFFSET (ERemindersWidgetClass
, activated
),
1634 g_signal_accumulator_first_wins
,
1636 g_cclosure_marshal_generic
,
1638 E_TYPE_REMINDER_DATA
);
1642 e_reminders_widget_init (ERemindersWidget
*reminders
)
1644 reminders
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (reminders
, E_TYPE_REMINDERS_WIDGET
, ERemindersWidgetPrivate
);
1645 reminders
->priv
->settings
= g_settings_new ("org.gnome.evolution-data-server.calendar");
1646 reminders
->priv
->cancellable
= g_cancellable_new ();
1647 reminders
->priv
->is_empty
= TRUE
;
1648 reminders
->priv
->is_mapped
= FALSE
;
1652 * e_reminders_widget_new:
1653 * @watcher: an #EReminderWatcher
1655 * Creates a new instance of #ERemindersWidget. It adds its own reference
1658 * Returns: (transfer full): a new instance of #ERemindersWidget.
1663 e_reminders_widget_new (EReminderWatcher
*watcher
)
1665 g_return_val_if_fail (E_IS_REMINDER_WATCHER (watcher
), NULL
);
1667 return g_object_new (E_TYPE_REMINDERS_WIDGET
,
1673 * e_reminders_widget_get_watcher:
1674 * @reminders: an #ERemindersWidget
1676 * Returns: (transfer none): an #EReminderWatcher with which the @reminders had
1677 * been created. Do on unref it, it's owned by the @reminders.
1682 e_reminders_widget_get_watcher (ERemindersWidget
*reminders
)
1684 g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders
), NULL
);
1686 return reminders
->priv
->watcher
;
1690 * e_reminders_widget_get_settings:
1691 * @reminders: an #ERemindersWidget
1693 * Returns: (transfer none): a #GSettings pointing to org.gnome.evolution-data-server.calendar
1694 * used by the @reminders widget.
1699 e_reminders_widget_get_settings (ERemindersWidget
*reminders
)
1701 g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders
), NULL
);
1703 return reminders
->priv
->settings
;
1707 * e_reminders_widget_is_empty:
1708 * @reminders: an #ERemindersWidget
1710 * Returns: %TRUE, when there is no past reminder left, %FALSE otherwise.
1715 e_reminders_widget_is_empty (ERemindersWidget
*reminders
)
1717 g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders
), FALSE
);
1719 return reminders
->priv
->is_empty
;
1723 * e_reminders_widget_get_tree_view:
1724 * @reminders: an #ERemindersWidget
1726 * Returns: (transfer none): a #GtkTreeView with past reminders. It's owned
1727 * by the @reminders widget.
1732 e_reminders_widget_get_tree_view (ERemindersWidget
*reminders
)
1734 g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders
), NULL
);
1736 return reminders
->priv
->tree_view
;
1740 reminders_widget_error_response_cb (GtkInfoBar
*info_bar
,
1744 ERemindersWidget
*reminders
= user_data
;
1746 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1748 if (reminders
->priv
->info_bar
== info_bar
) {
1749 gtk_widget_destroy (GTK_WIDGET (reminders
->priv
->info_bar
));
1750 reminders
->priv
->info_bar
= NULL
;
1755 * e_reminders_widget_report_error:
1756 * @reminders: an #ERemindersWidget
1757 * @prefix: (nullable): an optional prefix to show before the error message, or %NULL for none
1758 * @error: (nullable): a #GError to show the message from in the UI, or %NULL for unknown error
1760 * Shows a warning in the GUI with the @error message, optionally prefixed
1761 * with @prefix. When @error is %NULL, an "Unknown error" message is shown
1767 e_reminders_widget_report_error (ERemindersWidget
*reminders
,
1768 const gchar
*prefix
,
1769 const GError
*error
)
1772 const gchar
*message
;
1775 g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders
));
1778 message
= error
->message
;
1780 message
= _("Unknown error");
1782 if (prefix
&& *prefix
) {
1783 if (gtk_widget_get_direction (GTK_WIDGET (reminders
)) == GTK_TEXT_DIR_RTL
)
1784 tmp
= g_strconcat (message
, " ", prefix
, NULL
);
1786 tmp
= g_strconcat (prefix
, " ", message
, NULL
);
1791 if (reminders
->priv
->info_bar
) {
1792 gtk_widget_destroy (GTK_WIDGET (reminders
->priv
->info_bar
));
1793 reminders
->priv
->info_bar
= NULL
;
1796 reminders
->priv
->info_bar
= GTK_INFO_BAR (gtk_info_bar_new ());
1797 gtk_info_bar_set_message_type (reminders
->priv
->info_bar
, GTK_MESSAGE_ERROR
);
1798 gtk_info_bar_set_show_close_button (reminders
->priv
->info_bar
, TRUE
);
1800 label
= GTK_LABEL (gtk_label_new (message
));
1801 gtk_label_set_max_width_chars (label
, 120);
1802 gtk_label_set_line_wrap (label
, TRUE
);
1803 gtk_label_set_selectable (label
, TRUE
);
1804 gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (reminders
->priv
->info_bar
)), GTK_WIDGET (label
));
1805 gtk_widget_show (GTK_WIDGET (label
));
1806 gtk_widget_show (GTK_WIDGET (reminders
->priv
->info_bar
));
1808 g_signal_connect (reminders
->priv
->info_bar
, "response", G_CALLBACK (reminders_widget_error_response_cb
), reminders
);
1810 gtk_grid_attach (GTK_GRID (reminders
), GTK_WIDGET (reminders
->priv
->info_bar
), 0, 2, 1, 1);