Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-to-do-pane.c
blobad432f1fea0dea0d8342f4d9a27be32b84d8066d
1 /*
2 * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include <glib.h>
21 #include <glib/gi18n-lib.h>
22 #include <gtk/gtk.h>
24 #include <libedataserverui/libedataserverui.h>
25 #include <libecal/libecal.h>
27 #include "e-cal-data-model.h"
28 #include "e-cal-data-model-subscriber.h"
29 #include "e-cal-dialogs.h"
30 #include "e-cal-ops.h"
31 #include "misc.h"
33 #include "e-to-do-pane.h"
35 #define N_ROOTS 8
36 #define MAX_TOOLTIP_DESCRIPTION_LEN 128
38 struct _EToDoPanePrivate {
39 GWeakRef shell_view_weakref; /* EShellView * */
40 gboolean highlight_overdue;
41 GdkRGBA *overdue_color;
42 gboolean show_completed_tasks;
43 gboolean show_no_duedate_tasks;
44 gboolean use_24hour_format;
46 EClientCache *client_cache;
47 ESourceRegistryWatcher *watcher;
48 GtkTreeStore *tree_store;
49 GtkTreeView *tree_view;
50 ECalDataModel *events_data_model;
51 ECalDataModel *tasks_data_model;
52 GHashTable *component_refs; /* ComponentIdent * ~> GSList * { GtkTreeRowRefenrece * } */
53 GHashTable *client_colors; /* ESource * ~> GdkRGBA * */
55 GCancellable *cancellable;
57 guint time_checker_id;
58 guint last_today;
59 time_t nearest_due;
61 gulong source_changed_id;
63 GtkTreeRowReference *roots[N_ROOTS];
66 enum {
67 PROP_0,
68 PROP_HIGHLIGHT_OVERDUE,
69 PROP_OVERDUE_COLOR,
70 PROP_SHELL_VIEW,
71 PROP_SHOW_COMPLETED_TASKS,
72 PROP_SHOW_NO_DUEDATE_TASKS,
73 PROP_USE_24HOUR_FORMAT
76 static void e_to_do_pane_cal_data_model_subscriber_init (ECalDataModelSubscriberInterface *iface);
78 G_DEFINE_TYPE_WITH_CODE (EToDoPane, e_to_do_pane, GTK_TYPE_GRID,
79 G_IMPLEMENT_INTERFACE (E_TYPE_CAL_DATA_MODEL_SUBSCRIBER, e_to_do_pane_cal_data_model_subscriber_init))
81 enum {
82 COLUMN_BGCOLOR = 0,
83 COLUMN_FGCOLOR,
84 COLUMN_HAS_ICON_NAME,
85 COLUMN_ICON_NAME,
86 COLUMN_SUMMARY,
87 COLUMN_TOOLTIP,
88 COLUMN_SORTKEY,
89 COLUMN_DATE_MARK,
90 COLUMN_CAL_CLIENT,
91 COLUMN_CAL_COMPONENT,
92 N_COLUMNS
95 typedef struct _ComponentIdent {
96 gconstpointer client;
97 gchar *uid;
98 gchar *rid;
99 } ComponentIdent;
101 static ComponentIdent *
102 component_ident_new (gconstpointer client,
103 const gchar *uid,
104 const gchar *rid)
106 ComponentIdent *ci;
108 ci = g_new0 (ComponentIdent, 1);
109 ci->client = client;
110 ci->uid = g_strdup (uid);
111 ci->rid = (rid && *rid) ? g_strdup (rid) : NULL;
113 return ci;
116 static ComponentIdent *
117 component_ident_copy (const ComponentIdent *src)
119 if (!src)
120 return NULL;
122 return component_ident_new (src->client, src->uid, src->rid);
125 static void
126 component_ident_free (gpointer ptr)
128 ComponentIdent *ci = ptr;
130 if (ci) {
131 g_free (ci->uid);
132 g_free (ci->rid);
133 g_free (ci);
137 static guint
138 component_ident_hash (gconstpointer ptr)
140 const ComponentIdent *ci = ptr;
142 if (!ci)
143 return 0;
145 return g_direct_hash (ci->client) ^
146 (ci->uid ? g_str_hash (ci->uid) : 0) ^
147 (ci->rid ? g_str_hash (ci->rid) : 0);
150 static gboolean
151 component_ident_equal (gconstpointer ptr1,
152 gconstpointer ptr2)
154 const ComponentIdent *ci1 = ptr1, *ci2 = ptr2;
156 if (!ci1 || !ci2)
157 return ci1 == ci2;
159 return ci1->client == ci2->client &&
160 g_strcmp0 (ci1->uid, ci2->uid) == 0 &&
161 g_strcmp0 (ci1->rid, ci2->rid) == 0;
164 static void
165 etdp_free_component_refs (gpointer ptr)
167 GSList *roots = ptr;
169 g_slist_free_full (roots, (GDestroyNotify) gtk_tree_row_reference_free);
172 static guint
173 etdp_create_date_mark (const struct icaltimetype *itt)
175 if (!itt)
176 return 0;
178 return itt->year * 10000 + itt->month * 100 + itt->day;
181 static void
182 etdp_itt_to_zone (struct icaltimetype *itt,
183 const gchar *itt_tzid,
184 ECalClient *client,
185 icaltimezone *default_zone)
187 icaltimezone *zone = NULL;
189 g_return_if_fail (itt != NULL);
191 if (itt_tzid) {
192 e_cal_client_get_timezone_sync (client, itt_tzid, &zone, NULL, NULL);
193 } else if (icaltime_is_utc (*itt)) {
194 zone = icaltimezone_get_utc_timezone ();
197 if (zone)
198 icaltimezone_convert_time (itt, zone, default_zone);
201 static gchar *
202 etdp_date_time_to_string (const ECalComponentDateTime *dt,
203 ECalClient *client,
204 icaltimezone *default_zone,
205 guint today_date_mark,
206 gboolean is_task,
207 gboolean use_24hour_format,
208 struct icaltimetype *out_itt)
210 gboolean is_overdue;
211 gchar *res;
213 g_return_val_if_fail (dt != NULL, NULL);
214 g_return_val_if_fail (dt->value != NULL, NULL);
215 g_return_val_if_fail (out_itt != NULL, NULL);
217 *out_itt = *dt->value;
219 etdp_itt_to_zone (out_itt, dt->tzid, client, default_zone);
221 is_overdue = is_task && etdp_create_date_mark (out_itt) < today_date_mark;
223 if (out_itt->is_date && !is_overdue)
224 return NULL;
226 if (is_overdue) {
227 struct tm tm;
229 tm = icaltimetype_to_tm (out_itt);
231 res = e_datetime_format_format_tm ("calendar", "table", out_itt->is_date ? DTFormatKindDate : DTFormatKindDateTime, &tm);
232 } else {
233 if (use_24hour_format) {
234 res = g_strdup_printf ("%d:%02d", out_itt->hour, out_itt->minute);
235 } else {
236 gint hour = out_itt->hour;
237 const gchar *suffix;
239 if (hour < 12) {
240 /* String to use in 12-hour time format for times in the morning. */
241 suffix = _("am");
242 } else {
243 hour -= 12;
244 /* String to use in 12-hour time format for times in the afternoon. */
245 suffix = _("pm");
248 if (hour == 0)
249 hour = 12;
251 if (!out_itt->minute)
252 res = g_strdup_printf ("%d %s", hour, suffix);
253 else
254 res = g_strdup_printf ("%d:%02d %s", hour, out_itt->minute, suffix);
258 return res;
261 static void
262 etdp_append_to_string_escaped (GString *str,
263 const gchar *format,
264 const gchar *value1,
265 const gchar *value2)
267 gchar *escaped;
269 g_return_if_fail (str != NULL);
270 g_return_if_fail (format != NULL);
272 if (!value1 || !*value1)
273 return;
275 escaped = g_markup_printf_escaped (format, value1, value2);
276 g_string_append (str, escaped);
277 g_free (escaped);
280 static gchar *
281 etdp_format_date_time (ECalClient *client,
282 icaltimezone *default_zone,
283 const struct icaltimetype *in_itt,
284 const gchar *tzid)
286 struct icaltimetype itt;
287 struct tm tm;
289 if (!in_itt)
290 return NULL;
292 itt = *in_itt;
294 etdp_itt_to_zone (&itt, tzid, client, default_zone);
296 tm = icaltimetype_to_tm (&itt);
298 return e_datetime_format_format_tm ("calendar", "table", itt.is_date ? DTFormatKindDate : DTFormatKindDateTime, &tm);
301 static gboolean
302 etdp_get_component_data (EToDoPane *to_do_pane,
303 ECalClient *client,
304 ECalComponent *comp,
305 icaltimezone *default_zone,
306 guint today_date_mark,
307 gchar **out_summary,
308 gchar **out_tooltip,
309 gboolean *out_is_task,
310 gboolean *out_is_completed,
311 gchar **out_sort_key,
312 guint *out_date_mark)
314 icalcomponent *icalcomp;
315 ECalComponentDateTime dt = { 0 };
316 ECalComponentId *id;
317 struct icaltimetype itt = icaltime_null_time ();
318 const gchar *prefix, *location, *description;
319 gboolean task_has_due_date = TRUE; /* ignored for events, thus like being set */
320 GString *tooltip;
322 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
323 g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
324 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
325 g_return_val_if_fail (out_summary, FALSE);
326 g_return_val_if_fail (out_tooltip, FALSE);
327 g_return_val_if_fail (out_is_task, FALSE);
328 g_return_val_if_fail (out_is_completed, FALSE);
329 g_return_val_if_fail (out_sort_key, FALSE);
330 g_return_val_if_fail (out_date_mark, FALSE);
332 icalcomp = e_cal_component_get_icalcomponent (comp);
333 g_return_val_if_fail (icalcomp != NULL, FALSE);
335 location = icalcomponent_get_location (icalcomp);
336 if (location && !*location)
337 location = NULL;
339 tooltip = g_string_sized_new (512);
341 etdp_append_to_string_escaped (tooltip, "<b>%s</b>", icalcomponent_get_summary (icalcomp), NULL);
343 if (location) {
344 g_string_append (tooltip, "\n");
345 /* Translators: It will display "Location: LocationOfTheAppointment" */
346 etdp_append_to_string_escaped (tooltip, _("Location: %s"), location, NULL);
349 *out_is_task = e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_TODO;
350 *out_is_completed = FALSE;
352 if (*out_is_task) {
353 ECalComponentDateTime dtstart = { 0 };
354 struct icaltimetype *completed = NULL;
356 /* Tasks after events */
357 prefix = "1";
359 e_cal_component_get_dtstart (comp, &dtstart);
360 e_cal_component_get_due (comp, &dt);
361 e_cal_component_get_completed (comp, &completed);
363 if (dtstart.value) {
364 gchar *tmp;
366 tmp = etdp_format_date_time (client, default_zone, dtstart.value, dtstart.tzid);
368 g_string_append (tooltip, "\n");
369 /* Translators: It will display "Start: StartDateAndTime" */
370 etdp_append_to_string_escaped (tooltip, _("Start: %s"), tmp, NULL);
372 g_free (tmp);
374 if (!dt.value) {
375 /* Fill the itt structure in case the task has no Due date */
376 itt = *dtstart.value;
377 etdp_itt_to_zone (&itt, dtstart.tzid, client, default_zone);
380 e_cal_component_free_datetime (&dtstart);
383 if (dt.value) {
384 gchar *tmp;
386 tmp = etdp_format_date_time (client, default_zone, dt.value, dt.tzid);
388 g_string_append (tooltip, "\n");
389 /* Translators: It will display "Due: DueDateAndTime" */
390 etdp_append_to_string_escaped (tooltip, _("Due: %s"), tmp, NULL);
392 g_free (tmp);
393 } else {
394 task_has_due_date = FALSE;
397 if (completed) {
398 gchar *tmp;
400 tmp = etdp_format_date_time (client, default_zone, completed, NULL);
402 g_string_append (tooltip, "\n");
403 /* Translators: It will display "Completed: DateAndTimeWhenCompleted" */
404 etdp_append_to_string_escaped (tooltip, _("Completed: %s"), tmp, NULL);
406 g_free (tmp);
408 *out_is_completed = TRUE;
409 e_cal_component_free_icaltimetype (completed);
410 } else {
411 icalproperty_status status = ICAL_STATUS_NONE;
413 e_cal_component_get_status (comp, &status);
414 *out_is_completed = *out_is_completed || status == ICAL_STATUS_COMPLETED;
416 } else {
417 /* Events first */
418 prefix = "0";
420 e_cal_component_get_dtstart (comp, &dt);
422 if (dt.value) {
423 ECalComponentDateTime dtend = { 0 };
424 struct icaltimetype ittstart, ittend;
425 gchar *strstart, *strduration;
427 e_cal_component_get_dtend (comp, &dtend);
429 ittstart = *dt.value;
430 if (dtend.value)
431 ittend = *dtend.value;
432 else
433 ittend = ittstart;
435 etdp_itt_to_zone (&ittstart, dt.tzid, client, default_zone);
436 etdp_itt_to_zone (&ittend, dtend.value ? dtend.tzid : dt.tzid, client, default_zone);
438 strstart = etdp_format_date_time (client, default_zone, &ittstart, NULL);
439 strduration = calculate_time (icaltime_as_timet (ittstart), icaltime_as_timet (ittend));
441 g_string_append (tooltip, "\n");
442 /* Translators: It will display "Time: StartDateAndTime (Duration)" */
443 etdp_append_to_string_escaped (tooltip, _("Time: %s %s"), strstart, strduration);
445 g_free (strduration);
446 g_free (strstart);
448 e_cal_component_free_datetime (&dtend);
452 *out_summary = NULL;
454 if (dt.value) {
455 gchar *time_str;
457 time_str = etdp_date_time_to_string (&dt, client, default_zone, today_date_mark, *out_is_task,
458 to_do_pane->priv->use_24hour_format, &itt);
460 if (time_str) {
461 *out_summary = g_markup_printf_escaped ("<span size=\"xx-small\">%s</span> %s%s%s%s",
462 time_str, icalcomponent_get_summary (icalcomp), location ? " (" : "",
463 location ? location : "", location ? ")" : "");
466 g_free (time_str);
469 if (!*out_summary) {
470 *out_summary = g_markup_printf_escaped ("%s%s%s%s", icalcomponent_get_summary (icalcomp),
471 location ? " (" : "", location ? location : "", location ? ")" : "");
474 if (*out_is_completed) {
475 gchar *tmp = *out_summary;
477 /* With leading space, to have proper row height in GtkTreeView */
478 *out_summary = g_strdup_printf (" <s>%s</s>", *out_summary);
480 g_free (tmp);
481 } else {
482 gchar *tmp = *out_summary;
484 /* With leading space, to have proper row height in GtkTreeView */
485 *out_summary = g_strconcat (" ", *out_summary, NULL);
487 g_free (tmp);
490 e_cal_component_free_datetime (&dt);
492 id = e_cal_component_get_id (comp);
494 if (!task_has_due_date) {
495 if (icaltime_is_null_time (itt)) {
496 /* Sort those without Start date after those with it */
497 *out_sort_key = g_strdup_printf ("%s-Z-%s-%s-%s",
498 prefix, icalcomponent_get_summary (icalcomp),
499 (id && id->uid) ? id->uid : "", (id && id->rid) ? id->rid : "");
500 } else {
501 *out_sort_key = g_strdup_printf ("%s-%04d%02d%02d%02d%02d%02d-%s-%s-%s",
502 prefix, itt.year, itt.month, itt.day, itt.hour, itt.minute, itt.second,
503 icalcomponent_get_summary (icalcomp),
504 (id && id->uid) ? id->uid : "", (id && id->rid) ? id->rid : "");
506 } else {
507 *out_sort_key = g_strdup_printf ("%s-%04d%02d%02d%02d%02d%02d-%s-%s",
508 prefix, itt.year, itt.month, itt.day, itt.hour, itt.minute, itt.second,
509 (id && id->uid) ? id->uid : "", (id && id->rid) ? id->rid : "");
512 if (id)
513 e_cal_component_free_id (id);
515 description = icalcomponent_get_description (icalcomp);
516 if (description && *description && g_utf8_validate (description, -1, NULL)) {
517 gchar *tmp = NULL;
518 glong len;
520 len = g_utf8_strlen (description, -1);
521 if (len > MAX_TOOLTIP_DESCRIPTION_LEN) {
522 GString *str;
523 const gchar *end;
525 end = g_utf8_offset_to_pointer (description, MAX_TOOLTIP_DESCRIPTION_LEN);
526 str = g_string_new_len (description, end - description);
527 g_string_append (str, "…");
529 tmp = g_string_free (str, FALSE);
532 g_string_append (tooltip, "\n\n");
533 etdp_append_to_string_escaped (tooltip, "%s", tmp ? tmp : description, NULL);
535 g_free (tmp);
538 *out_date_mark = etdp_create_date_mark (&itt);
539 *out_tooltip = g_string_free (tooltip, FALSE);
541 return TRUE;
544 static GdkRGBA
545 etdp_get_fgcolor_for_bgcolor (const GdkRGBA *bgcolor)
547 GdkRGBA fgcolor = { 1.0, 1.0, 1.0, 1.0 };
549 if (bgcolor) {
550 if ((bgcolor->red > 0.7) || (bgcolor->green > 0.7) || (bgcolor->blue > 0.7)) {
551 fgcolor.red = 0.0;
552 fgcolor.green = 0.0;
553 fgcolor.blue = 0.0;
554 } else {
555 fgcolor.red = 1.0;
556 fgcolor.green = 1.0;
557 fgcolor.blue = 1.0;
561 return fgcolor;
564 static GSList * /* GtkTreePath * */
565 etdp_get_component_root_paths (EToDoPane *to_do_pane,
566 ECalClient *client,
567 ECalComponent *comp,
568 icaltimezone *default_zone)
570 ECalComponentDateTime dt;
571 struct icaltimetype itt;
572 GtkTreePath *first_root_path = NULL;
573 GtkTreeModel *model;
574 GSList *roots = NULL;
575 guint start_date_mark, end_date_mark, prev_date_mark = 0;
576 gint ii;
578 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), NULL);
579 g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
580 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
582 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_TODO) {
583 e_cal_component_get_due (comp, &dt);
585 if (dt.value) {
586 itt = *dt.value;
588 etdp_itt_to_zone (&itt, dt.tzid, client, default_zone);
589 start_date_mark = etdp_create_date_mark (&itt);
590 } else {
591 start_date_mark = 0;
594 end_date_mark = start_date_mark;
596 e_cal_component_free_datetime (&dt);
597 } else {
598 e_cal_component_get_dtstart (comp, &dt);
600 if (dt.value) {
601 itt = *dt.value;
603 etdp_itt_to_zone (&itt, dt.tzid, client, default_zone);
604 start_date_mark = etdp_create_date_mark (&itt);
605 } else {
606 start_date_mark = 0;
609 e_cal_component_free_datetime (&dt);
611 e_cal_component_get_dtend (comp, &dt);
613 if (dt.value) {
614 itt = *dt.value;
616 etdp_itt_to_zone (&itt, dt.tzid, client, default_zone);
617 end_date_mark = etdp_create_date_mark (&itt);
618 } else {
619 end_date_mark = start_date_mark;
622 e_cal_component_free_datetime (&dt);
625 model = GTK_TREE_MODEL (to_do_pane->priv->tree_store);
627 if (start_date_mark == 0 && e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_TODO) {
628 if (!to_do_pane->priv->show_no_duedate_tasks)
629 return NULL;
631 if (gtk_tree_row_reference_valid (to_do_pane->priv->roots[N_ROOTS - 1])) {
632 GtkTreePath *root_path;
633 GtkTreeIter root_iter;
635 root_path = gtk_tree_row_reference_get_path (to_do_pane->priv->roots[N_ROOTS - 1]);
636 if (root_path && gtk_tree_model_get_iter (model, &root_iter, root_path)) {
637 roots = g_slist_prepend (roots, root_path);
638 root_path = NULL;
641 gtk_tree_path_free (root_path);
644 return roots;
647 for (ii = 0; ii < N_ROOTS - 1; ii++) {
648 if (gtk_tree_row_reference_valid (to_do_pane->priv->roots[ii])) {
649 GtkTreePath *root_path;
650 GtkTreeIter root_iter;
651 guint root_date_mark = 0;
653 root_path = gtk_tree_row_reference_get_path (to_do_pane->priv->roots[ii]);
654 if (root_path && gtk_tree_model_get_iter (model, &root_iter, root_path)) {
655 gtk_tree_model_get (model, &root_iter, COLUMN_DATE_MARK, &root_date_mark, -1);
657 if (start_date_mark < root_date_mark && (end_date_mark > prev_date_mark ||
658 (start_date_mark == end_date_mark && end_date_mark >= prev_date_mark))) {
659 roots = g_slist_prepend (roots, gtk_tree_path_copy (root_path));
660 } else if (!first_root_path) {
661 first_root_path = gtk_tree_path_copy (root_path);
664 prev_date_mark = root_date_mark;
667 gtk_tree_path_free (root_path);
671 if (!roots && first_root_path && start_date_mark < prev_date_mark)
672 roots = g_slist_prepend (roots, first_root_path);
673 else
674 gtk_tree_path_free (first_root_path);
676 return g_slist_reverse (roots);
679 static GSList * /* GtkTreeRowReference * */
680 etdp_merge_with_root_paths (EToDoPane *to_do_pane,
681 GtkTreeModel *model,
682 const GSList *new_root_paths, /* GtkTreePath * */
683 const GSList *current_refs) /* GtkTreeRowReference * */
685 GSList *new_references = NULL;
686 const GSList *paths_link, *refs_link;
687 GtkTreeIter iter, parent;
689 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), NULL);
690 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
691 g_return_val_if_fail (new_root_paths != NULL, NULL);
693 refs_link = current_refs;
694 for (paths_link = new_root_paths; paths_link; paths_link = g_slist_next (paths_link)) {
695 GtkTreePath *root_path = paths_link->data;
696 gboolean found = FALSE;
698 while (refs_link && !found) {
699 GtkTreeRowReference *reference = refs_link->data;
700 GtkTreePath *ref_path;
702 ref_path = gtk_tree_row_reference_get_path (reference);
703 if (ref_path &&
704 gtk_tree_model_get_iter (model, &iter, ref_path) &&
705 gtk_tree_model_iter_parent (model, &parent, &iter)) {
706 GtkTreePath *parent_path;
707 gint cmp;
709 parent_path = gtk_tree_model_get_path (model, &parent);
710 cmp = gtk_tree_path_compare (parent_path, root_path);
711 gtk_tree_path_free (parent_path);
713 if (cmp == 0) {
714 found = TRUE;
715 new_references = g_slist_prepend (new_references, gtk_tree_row_reference_copy (reference));
716 } else if (cmp > 0) {
717 gtk_tree_path_free (ref_path);
718 break;
719 } else {
720 gtk_tree_store_remove (to_do_pane->priv->tree_store, &iter);
723 gtk_tree_path_free (ref_path);
725 refs_link = g_slist_next (refs_link);
728 if (!found) {
729 GtkTreeIter parent;
730 GtkTreePath *path;
732 g_warn_if_fail (gtk_tree_model_get_iter (model, &parent, root_path));
734 gtk_tree_store_append (to_do_pane->priv->tree_store, &iter, &parent);
735 path = gtk_tree_model_get_path (model, &iter);
737 if (gtk_tree_model_iter_n_children (model, &parent) == 1) {
738 gtk_tree_view_expand_row (to_do_pane->priv->tree_view, root_path, TRUE);
741 new_references = g_slist_prepend (new_references, gtk_tree_row_reference_new (model, path));
743 gtk_tree_path_free (path);
747 while (refs_link) {
748 GtkTreeRowReference *reference = refs_link->data;
749 GtkTreePath *ref_path;
751 ref_path = gtk_tree_row_reference_get_path (reference);
752 if (ref_path &&
753 gtk_tree_model_get_iter (model, &iter, ref_path)) {
754 gtk_tree_store_remove (to_do_pane->priv->tree_store, &iter);
757 gtk_tree_path_free (ref_path);
759 refs_link = g_slist_next (refs_link);
762 g_warn_if_fail (g_slist_length (new_references) == g_slist_length ((GSList *) new_root_paths));
764 return g_slist_reverse (new_references);
767 static void
768 etdp_get_comp_colors (EToDoPane *to_do_pane,
769 ECalClient *client,
770 ECalComponent *comp,
771 GdkRGBA *out_bgcolor,
772 gboolean *out_bgcolor_set,
773 GdkRGBA *out_fgcolor,
774 gboolean *out_fgcolor_set,
775 time_t *out_nearest_due)
777 GdkRGBA *bgcolor, fgcolor;
779 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
780 g_return_if_fail (out_bgcolor);
781 g_return_if_fail (out_bgcolor_set);
782 g_return_if_fail (out_fgcolor);
783 g_return_if_fail (out_fgcolor_set);
785 *out_bgcolor_set = FALSE;
786 *out_fgcolor_set = FALSE;
788 g_return_if_fail (E_IS_CAL_CLIENT (client));
789 g_return_if_fail (E_IS_CAL_COMPONENT (comp));
791 bgcolor = g_hash_table_lookup (to_do_pane->priv->client_colors, e_client_get_source (E_CLIENT (client)));
793 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_TODO &&
794 to_do_pane->priv->highlight_overdue &&
795 to_do_pane->priv->overdue_color) {
796 ECalComponentDateTime dt = { 0 };
798 e_cal_component_get_due (comp, &dt);
800 if (dt.value) {
801 icaltimezone *default_zone;
802 struct icaltimetype itt, now;
804 default_zone = e_cal_data_model_get_timezone (to_do_pane->priv->events_data_model);
806 itt = *dt.value;
807 etdp_itt_to_zone (&itt, dt.tzid, client, default_zone);
809 now = icaltime_current_time_with_zone (default_zone);
811 if ((dt.value->is_date && icaltime_compare_date_only (itt, now) < 0) ||
812 (!dt.value->is_date && icaltime_compare (itt, now) <= 0)) {
813 bgcolor = to_do_pane->priv->overdue_color;
814 } else if (out_nearest_due) {
815 time_t due_tt;
817 due_tt = icaltime_as_timet_with_zone (itt, default_zone);
818 if (*out_nearest_due == (time_t) -1 ||
819 *out_nearest_due > due_tt)
820 *out_nearest_due = due_tt;
824 e_cal_component_free_datetime (&dt);
827 fgcolor = etdp_get_fgcolor_for_bgcolor (bgcolor);
829 *out_bgcolor_set = bgcolor != NULL;
830 if (bgcolor)
831 *out_bgcolor = *bgcolor;
833 *out_fgcolor_set = *out_bgcolor_set;
834 *out_fgcolor = fgcolor;
837 static void
838 etdp_add_component (EToDoPane *to_do_pane,
839 ECalClient *client,
840 ECalComponent *comp)
842 ECalComponentId *id;
843 ComponentIdent *ident;
844 icaltimezone *default_zone;
845 GSList *new_root_paths, *new_references, *link;
846 GtkTreeModel *model;
847 GtkTreeIter iter = { 0 };
848 GdkRGBA bgcolor, fgcolor;
849 gboolean bgcolor_set = FALSE, fgcolor_set = FALSE;
850 gchar *summary = NULL, *tooltip = NULL, *sort_key = NULL;
851 gboolean is_task = FALSE, is_completed = FALSE;
852 const gchar *icon_name;
853 guint date_mark = 0;
855 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
856 g_return_if_fail (E_IS_CAL_CLIENT (client));
857 g_return_if_fail (E_IS_CAL_COMPONENT (comp));
859 id = e_cal_component_get_id (comp);
860 g_return_if_fail (id != NULL);
862 default_zone = e_cal_data_model_get_timezone (to_do_pane->priv->events_data_model);
864 if (!etdp_get_component_data (to_do_pane, client, comp, default_zone, to_do_pane->priv->last_today,
865 &summary, &tooltip, &is_task, &is_completed, &sort_key, &date_mark)) {
866 e_cal_component_free_id (id);
867 return;
870 model = GTK_TREE_MODEL (to_do_pane->priv->tree_store);
871 ident = component_ident_new (client, id->uid, id->rid);
873 new_root_paths = etdp_get_component_root_paths (to_do_pane, client, comp, default_zone);
875 new_references = etdp_merge_with_root_paths (to_do_pane, model, new_root_paths,
876 g_hash_table_lookup (to_do_pane->priv->component_refs, ident));
878 g_slist_free_full (new_root_paths, (GDestroyNotify) gtk_tree_path_free);
880 if (e_cal_component_has_attendees (comp)) {
881 if (is_task)
882 icon_name = "stock_task-assigned";
883 else
884 icon_name = "stock_people";
885 } else {
886 if (is_task)
887 icon_name = "stock_task";
888 else
889 icon_name = "appointment-new";
892 etdp_get_comp_colors (to_do_pane, client, comp, &bgcolor, &bgcolor_set, &fgcolor, &fgcolor_set,
893 &to_do_pane->priv->nearest_due);
895 for (link = new_references; link; link = g_slist_next (link)) {
896 GtkTreeRowReference *reference = link->data;
898 if (gtk_tree_row_reference_valid (reference)) {
899 GtkTreePath *path;
901 path = gtk_tree_row_reference_get_path (reference);
902 if (path && gtk_tree_model_get_iter (model, &iter, path)) {
903 gtk_tree_store_set (to_do_pane->priv->tree_store, &iter,
904 COLUMN_BGCOLOR, bgcolor_set ? &bgcolor : NULL,
905 COLUMN_FGCOLOR, fgcolor_set ? &fgcolor : NULL,
906 COLUMN_HAS_ICON_NAME, TRUE,
907 COLUMN_ICON_NAME, icon_name,
908 COLUMN_SUMMARY, summary,
909 COLUMN_TOOLTIP, tooltip,
910 COLUMN_SORTKEY, sort_key,
911 COLUMN_DATE_MARK, date_mark,
912 COLUMN_CAL_CLIENT, client,
913 COLUMN_CAL_COMPONENT, comp,
914 -1);
917 gtk_tree_path_free (path);
921 g_hash_table_insert (to_do_pane->priv->component_refs, component_ident_copy (ident), new_references);
923 component_ident_free (ident);
924 e_cal_component_free_id (id);
925 g_free (summary);
926 g_free (tooltip);
927 g_free (sort_key);
930 static void
931 etdp_got_client_cb (GObject *source_object,
932 GAsyncResult *result,
933 gpointer user_data)
935 EToDoPane *to_do_pane = user_data;
936 EClient *client;
937 GError *error = NULL;
939 client = e_client_cache_get_client_finish (E_CLIENT_CACHE (source_object), result, &error);
941 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
942 g_clear_error (&error);
943 return;
946 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
948 if (client && gtk_widget_get_visible (GTK_WIDGET (to_do_pane))) {
949 ECalClient *cal_client = E_CAL_CLIENT (client);
950 ESource *source;
951 ESourceSelectable *selectable = NULL;
952 ECalDataModel *data_model = NULL;
954 g_warn_if_fail (cal_client != NULL);
956 source = e_client_get_source (client);
958 if (e_cal_client_get_source_type (cal_client) == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
959 selectable = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
960 data_model = to_do_pane->priv->events_data_model;
961 } else if (e_cal_client_get_source_type (cal_client) == E_CAL_CLIENT_SOURCE_TYPE_TASKS) {
962 selectable = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
963 data_model = to_do_pane->priv->tasks_data_model;
966 if (data_model) {
967 g_hash_table_remove (to_do_pane->priv->client_colors, source);
968 if (selectable) {
969 GdkRGBA rgba;
970 gchar *color_spec;
972 color_spec = e_source_selectable_dup_color (selectable);
973 if (color_spec && gdk_rgba_parse (&rgba, color_spec)) {
974 g_hash_table_insert (to_do_pane->priv->client_colors, source, gdk_rgba_copy (&rgba));
977 g_free (color_spec);
980 e_cal_data_model_add_client (data_model, cal_client);
982 } else if (!client) {
983 /* Ignore errors */
986 g_clear_object (&client);
987 g_clear_error (&error);
990 static gboolean
991 e_to_do_pane_watcher_filter_cb (ESourceRegistryWatcher *watcher,
992 ESource *source,
993 gpointer user_data)
995 ESourceSelectable *selectable = NULL;
997 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
999 if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
1000 selectable = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
1001 else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
1002 selectable = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
1004 return selectable && e_source_selectable_get_selected (selectable);
1007 static void
1008 e_to_do_pane_watcher_appeared_cb (ESourceRegistryWatcher *watcher,
1009 ESource *source,
1010 gpointer user_data)
1012 EToDoPane *to_do_pane = user_data;
1013 const gchar *extension_name = NULL;
1015 g_return_if_fail (E_IS_SOURCE (source));
1016 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1018 if (!gtk_widget_get_visible (GTK_WIDGET (to_do_pane)))
1019 return;
1021 if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
1022 extension_name = E_SOURCE_EXTENSION_CALENDAR;
1023 else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
1024 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1026 g_return_if_fail (extension_name != NULL);
1028 e_client_cache_get_client (to_do_pane->priv->client_cache, source, extension_name,
1029 (guint32) -1, to_do_pane->priv->cancellable, etdp_got_client_cb, to_do_pane);
1032 static void
1033 e_to_do_pane_watcher_disappeared_cb (ESourceRegistryWatcher *watcher,
1034 ESource *source,
1035 gpointer user_data)
1037 EToDoPane *to_do_pane = user_data;
1039 g_return_if_fail (E_IS_SOURCE (source));
1040 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1042 g_hash_table_remove (to_do_pane->priv->client_colors, source);
1044 if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
1045 e_cal_data_model_remove_client (to_do_pane->priv->events_data_model, e_source_get_uid (source));
1046 else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
1047 e_cal_data_model_remove_client (to_do_pane->priv->tasks_data_model, e_source_get_uid (source));
1050 static void
1051 etdp_data_subscriber_component_added (ECalDataModelSubscriber *subscriber,
1052 ECalClient *client,
1053 ECalComponent *comp)
1055 g_return_if_fail (E_IS_TO_DO_PANE (subscriber));
1057 etdp_add_component (E_TO_DO_PANE (subscriber), client, comp);
1060 static void
1061 etdp_data_subscriber_component_modified (ECalDataModelSubscriber *subscriber,
1062 ECalClient *client,
1063 ECalComponent *comp)
1065 g_return_if_fail (E_IS_TO_DO_PANE (subscriber));
1067 etdp_add_component (E_TO_DO_PANE (subscriber), client, comp);
1070 static void
1071 etdp_data_subscriber_component_removed (ECalDataModelSubscriber *subscriber,
1072 ECalClient *client,
1073 const gchar *uid,
1074 const gchar *rid)
1076 EToDoPane *to_do_pane;
1077 ComponentIdent ident;
1078 GSList *link;
1080 g_return_if_fail (E_IS_TO_DO_PANE (subscriber));
1082 to_do_pane = E_TO_DO_PANE (subscriber);
1084 ident.client = client;
1085 ident.uid = (gchar *) uid;
1086 ident.rid = (gchar *) (rid && *rid ? rid : NULL);
1088 for (link = g_hash_table_lookup (to_do_pane->priv->component_refs, &ident); link; link = g_slist_next (link)) {
1089 GtkTreeRowReference *reference = link->data;
1091 if (reference && gtk_tree_row_reference_valid (reference)) {
1092 GtkTreePath *path;
1093 GtkTreeIter iter;
1095 path = gtk_tree_row_reference_get_path (reference);
1097 if (path && gtk_tree_model_get_iter (gtk_tree_row_reference_get_model (reference), &iter, path)) {
1098 gtk_tree_store_remove (to_do_pane->priv->tree_store, &iter);
1101 gtk_tree_path_free (path);
1105 g_hash_table_remove (to_do_pane->priv->component_refs, &ident);
1108 static void
1109 etdp_data_subscriber_freeze (ECalDataModelSubscriber *subscriber)
1111 g_return_if_fail (E_IS_TO_DO_PANE (subscriber));
1114 static void
1115 etdp_data_subscriber_thaw (ECalDataModelSubscriber *subscriber)
1117 g_return_if_fail (E_IS_TO_DO_PANE (subscriber));
1120 static GCancellable *
1121 e_to_do_pane_submit_thread_job (GObject *responder,
1122 const gchar *description,
1123 const gchar *alert_ident,
1124 const gchar *alert_arg_0,
1125 EAlertSinkThreadJobFunc func,
1126 gpointer user_data,
1127 GDestroyNotify free_user_data)
1129 EShellView *shell_view;
1130 EActivity *activity;
1131 GCancellable *cancellable = NULL;
1133 g_return_val_if_fail (E_IS_TO_DO_PANE (responder), NULL);
1135 shell_view = e_to_do_pane_ref_shell_view (E_TO_DO_PANE (responder));
1136 if (!shell_view)
1137 return NULL;
1139 activity = e_shell_view_submit_thread_job (shell_view, description,
1140 alert_ident, alert_arg_0, func, user_data, free_user_data);
1142 if (activity) {
1143 cancellable = e_activity_get_cancellable (activity);
1144 if (cancellable)
1145 g_object_ref (cancellable);
1146 g_object_unref (activity);
1149 g_clear_object (&shell_view);
1151 return cancellable;
1154 static void
1155 etdp_update_all (EToDoPane *to_do_pane)
1157 GtkTreeModel *model;
1158 GtkTreeIter iter, next;
1159 gint level = 0;
1160 gboolean done = FALSE;
1161 GHashTable *comps_by_client; /* ECalClient ~> GHashTable { ECalComponent *, NULL } */
1162 GHashTableIter htiter;
1163 gpointer key, value;
1165 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1167 to_do_pane->priv->nearest_due = (time_t) -1;
1169 model = GTK_TREE_MODEL (to_do_pane->priv->tree_store);
1171 if (!gtk_tree_model_get_iter_first (model, &iter))
1172 return;
1174 comps_by_client = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) g_hash_table_unref);
1176 while (!done) {
1177 if (level != 0) {
1178 ECalClient *client = NULL;
1179 ECalComponent *comp = NULL;
1181 gtk_tree_model_get (model, &iter,
1182 COLUMN_CAL_CLIENT, &client,
1183 COLUMN_CAL_COMPONENT, &comp,
1184 -1);
1186 if (client && comp) {
1187 GHashTable *comps;
1189 comps = g_hash_table_lookup (comps_by_client, client);
1190 if (comps) {
1191 g_hash_table_ref (comps);
1192 } else {
1193 comps = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
1196 g_hash_table_insert (comps, g_object_ref (comp), NULL);
1197 g_hash_table_insert (comps_by_client, g_object_ref (client), comps);
1200 g_clear_object (&client);
1201 g_clear_object (&comp);
1204 done = !gtk_tree_model_iter_children (model, &next, &iter);
1206 if (done) {
1207 next = iter;
1208 done = !gtk_tree_model_iter_next (model, &next);
1209 } else {
1210 level++;
1213 if (done) {
1214 while (done = !gtk_tree_model_iter_parent (model, &next, &iter), !done) {
1215 level--;
1217 iter = next;
1218 done = !gtk_tree_model_iter_next (model, &next);
1220 if (!done)
1221 break;
1225 iter = next;
1228 g_hash_table_iter_init (&htiter, comps_by_client);
1229 while (g_hash_table_iter_next (&htiter, &key, &value)) {
1230 ECalClient *client = key;
1231 GHashTable *comps = value;
1232 GHashTableIter citer;
1234 g_hash_table_iter_init (&citer, comps);
1235 while (g_hash_table_iter_next (&citer, &key, NULL)) {
1236 ECalComponent *comp = key;
1238 etdp_add_component (to_do_pane, client, comp);
1242 g_hash_table_destroy (comps_by_client);
1245 static void
1246 etdp_update_colors (EToDoPane *to_do_pane,
1247 gboolean only_overdue)
1249 GtkTreeModel *model;
1250 GtkTreeIter iter, next;
1251 gint level = 0;
1252 time_t nearest_due = (time_t) -1;
1253 gboolean done = FALSE;
1255 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1257 model = GTK_TREE_MODEL (to_do_pane->priv->tree_store);
1259 if (!gtk_tree_model_get_iter_first (model, &iter))
1260 return;
1262 while (!done) {
1263 if (level != 0) {
1264 ECalClient *client = NULL;
1265 ECalComponent *comp = NULL;
1267 gtk_tree_model_get (model, &iter,
1268 COLUMN_CAL_CLIENT, &client,
1269 COLUMN_CAL_COMPONENT, &comp,
1270 -1);
1272 if (client && comp) {
1273 GdkRGBA bgcolor, fgcolor;
1274 gboolean bgcolor_set = FALSE, fgcolor_set = FALSE;
1276 etdp_get_comp_colors (to_do_pane, client, comp, &bgcolor, &bgcolor_set, &fgcolor, &fgcolor_set, &nearest_due);
1278 gtk_tree_store_set (to_do_pane->priv->tree_store, &iter,
1279 COLUMN_BGCOLOR, bgcolor_set ? &bgcolor : NULL,
1280 COLUMN_FGCOLOR, fgcolor_set ? &fgcolor : NULL,
1281 -1);
1284 g_clear_object (&client);
1285 g_clear_object (&comp);
1288 done = !gtk_tree_model_iter_children (model, &next, &iter);
1290 if (done) {
1291 next = iter;
1292 done = !gtk_tree_model_iter_next (model, &next);
1294 /* Overdue can be only those 'Today', thus under the first child. */
1295 if (only_overdue && !level)
1296 break;
1297 } else {
1298 level++;
1301 if (done) {
1302 while (done = !gtk_tree_model_iter_parent (model, &next, &iter), !done) {
1303 level--;
1305 iter = next;
1306 done = !gtk_tree_model_iter_next (model, &next);
1308 /* Overdue can be only those 'Today', thus under the first child. */
1309 if (only_overdue && !level)
1310 done = TRUE;
1312 if (!done)
1313 break;
1317 iter = next;
1320 to_do_pane->priv->nearest_due = nearest_due;
1323 static void
1324 etdp_check_time_changed (EToDoPane *to_do_pane,
1325 gboolean force_update)
1327 icaltimetype itt;
1328 icaltimezone *zone;
1329 guint new_today;
1331 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1333 zone = e_cal_data_model_get_timezone (to_do_pane->priv->events_data_model);
1334 itt = icaltime_current_time_with_zone (zone);
1335 new_today = etdp_create_date_mark (&itt);
1337 if (force_update || new_today != to_do_pane->priv->last_today) {
1338 gchar *tasks_filter;
1339 time_t tt_begin, tt_end;
1340 gchar *iso_begin_all, *iso_begin, *iso_end;
1341 gint ii;
1343 to_do_pane->priv->last_today = new_today;
1345 tt_begin = icaltime_as_timet_with_zone (itt, zone);
1346 tt_begin = time_day_begin_with_zone (tt_begin, zone);
1347 tt_end = time_add_week_with_zone (tt_begin, 1, zone) - 1;
1349 iso_begin_all = isodate_from_time_t (0);
1350 iso_begin = isodate_from_time_t (tt_begin);
1351 iso_end = isodate_from_time_t (tt_end);
1352 if (to_do_pane->priv->show_no_duedate_tasks) {
1353 if (to_do_pane->priv->show_completed_tasks) {
1354 tasks_filter = g_strdup ("#t");
1355 } else {
1356 tasks_filter = g_strdup ("(not (is-completed?))");
1358 } else if (to_do_pane->priv->show_completed_tasks) {
1359 tasks_filter = g_strdup_printf (
1360 "(or"
1361 " (and"
1362 " (not (is-completed?))"
1363 " (due-in-time-range? (make-time \"%s\") (make-time \"%s\"))"
1365 " (and"
1366 " (due-in-time-range? (make-time \"%s\") (make-time \"%s\"))"
1368 ")",
1369 iso_begin_all, iso_begin, iso_begin, iso_end);
1370 } else {
1371 tasks_filter = g_strdup_printf (
1372 "(and"
1373 " (not (is-completed?))"
1374 " (due-in-time-range? (make-time \"%s\") (make-time \"%s\"))"
1375 ")",
1376 iso_begin_all, iso_end);
1379 /* Re-label the roots */
1380 for (ii = 0; ii < N_ROOTS; ii++) {
1381 GtkTreePath *path;
1382 GtkTreeIter iter;
1384 if (!gtk_tree_row_reference_valid (to_do_pane->priv->roots[ii])) {
1385 if (ii == N_ROOTS - 1) {
1386 GtkTreeModel *model;
1387 gchar *sort_key;
1389 if (!to_do_pane->priv->show_no_duedate_tasks)
1390 continue;
1392 sort_key = g_strdup_printf ("%c", 'A' + ii);
1394 gtk_tree_store_append (to_do_pane->priv->tree_store, &iter, NULL);
1395 gtk_tree_store_set (to_do_pane->priv->tree_store, &iter,
1396 COLUMN_SORTKEY, sort_key,
1397 COLUMN_HAS_ICON_NAME, FALSE,
1398 -1);
1400 g_free (sort_key);
1402 model = GTK_TREE_MODEL (to_do_pane->priv->tree_store);
1403 path = gtk_tree_model_get_path (model, &iter);
1405 gtk_tree_row_reference_free (to_do_pane->priv->roots[ii]);
1406 to_do_pane->priv->roots[ii] = gtk_tree_row_reference_new (model, path);
1407 g_warn_if_fail (to_do_pane->priv->roots[ii] != NULL);
1409 gtk_tree_path_free (path);
1410 } else {
1411 continue;
1415 path = gtk_tree_row_reference_get_path (to_do_pane->priv->roots[ii]);
1417 if (gtk_tree_model_get_iter (gtk_tree_row_reference_get_model (to_do_pane->priv->roots[ii]), &iter, path)) {
1418 struct tm tm;
1419 gchar *markup;
1420 guint date_mark;
1422 tm = icaltimetype_to_tm (&itt);
1424 icaltime_adjust (&itt, 1, 0, 0, 0);
1426 date_mark = etdp_create_date_mark (&itt);
1428 if (ii == 0) {
1429 markup = g_markup_printf_escaped ("<b>%s</b>", _("Today"));
1430 } else if (ii == 1) {
1431 markup = g_markup_printf_escaped ("<b>%s</b>", _("Tomorrow"));
1432 } else if (ii == N_ROOTS - 1) {
1433 if (!to_do_pane->priv->show_no_duedate_tasks) {
1434 gtk_tree_store_remove (to_do_pane->priv->tree_store, &iter);
1435 gtk_tree_row_reference_free (to_do_pane->priv->roots[ii]);
1436 to_do_pane->priv->roots[ii] = NULL;
1437 gtk_tree_path_free (path);
1438 break;
1441 markup = g_markup_printf_escaped ("<b>%s</b>", _("Tasks without Due date"));
1442 } else {
1443 gchar *date;
1445 date = e_datetime_format_format_tm ("calendar", "table", DTFormatKindDate, &tm);
1446 markup = g_markup_printf_escaped ("<b>%s</b>", date);
1447 g_free (date);
1450 gtk_tree_store_set (to_do_pane->priv->tree_store, &iter,
1451 COLUMN_SUMMARY, markup,
1452 COLUMN_DATE_MARK, date_mark,
1453 -1);
1455 g_free (markup);
1456 } else {
1457 icaltime_adjust (&itt, 1, 0, 0, 0);
1460 gtk_tree_path_free (path);
1463 /* Update data-model-s */
1464 e_cal_data_model_subscribe (to_do_pane->priv->events_data_model,
1465 E_CAL_DATA_MODEL_SUBSCRIBER (to_do_pane), tt_begin, tt_end);
1467 e_cal_data_model_set_filter (to_do_pane->priv->tasks_data_model, tasks_filter);
1469 e_cal_data_model_subscribe (to_do_pane->priv->tasks_data_model,
1470 E_CAL_DATA_MODEL_SUBSCRIBER (to_do_pane), 0, 0);
1472 g_free (tasks_filter);
1473 g_free (iso_begin_all);
1474 g_free (iso_begin);
1475 g_free (iso_end);
1477 etdp_update_all (to_do_pane);
1478 } else {
1479 time_t now_tt = icaltime_as_timet_with_zone (itt, zone);
1481 if (to_do_pane->priv->nearest_due != (time_t) -1 &&
1482 to_do_pane->priv->nearest_due <= now_tt)
1483 etdp_update_colors (to_do_pane, TRUE);
1487 static gboolean
1488 etdp_check_time_cb (gpointer user_data)
1490 EToDoPane *to_do_pane = user_data;
1492 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
1494 etdp_check_time_changed (to_do_pane, FALSE);
1496 return TRUE;
1499 static void
1500 etdp_update_queries (EToDoPane *to_do_pane)
1502 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1504 etdp_check_time_changed (to_do_pane, TRUE);
1507 static void
1508 etdp_timezone_changed_cb (ECalDataModel *data_model,
1509 GParamSpec *param,
1510 gpointer user_data)
1512 EToDoPane *to_do_pane = user_data;
1514 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1516 etdp_check_time_changed (to_do_pane, TRUE);
1519 static gboolean
1520 etdp_settings_map_string_to_icaltimezone (GValue *value,
1521 GVariant *variant,
1522 gpointer user_data)
1524 GSettings *settings;
1525 const gchar *location = NULL;
1526 icaltimezone *timezone = NULL;
1528 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
1530 if (g_settings_get_boolean (settings, "use-system-timezone"))
1531 timezone = e_cal_util_get_system_timezone ();
1532 else
1533 location = g_variant_get_string (variant, NULL);
1535 if (location != NULL && *location != '\0')
1536 timezone = icaltimezone_get_builtin_timezone (location);
1538 if (timezone == NULL)
1539 timezone = icaltimezone_get_utc_timezone ();
1541 g_value_set_pointer (value, timezone);
1543 g_object_unref (settings);
1545 return TRUE;
1548 static gboolean
1549 etdp_settings_map_string_to_rgba (GValue *value,
1550 GVariant *variant,
1551 gpointer user_data)
1553 GdkRGBA rgba;
1554 const gchar *color_str;
1556 color_str = g_variant_get_string (variant, NULL);
1558 if (color_str && gdk_rgba_parse (&rgba, color_str))
1559 g_value_set_boxed (value, &rgba);
1560 else
1561 g_value_set_boxed (value, NULL);
1563 return TRUE;
1566 static void
1567 etdp_row_activated_cb (GtkTreeView *tree_view,
1568 GtkTreePath *path,
1569 GtkTreeViewColumn *column,
1570 gpointer user_data)
1572 EToDoPane *to_do_pane = user_data;
1573 GtkTreeModel *model;
1574 GtkTreeIter iter;
1576 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1578 model = gtk_tree_view_get_model (tree_view);
1580 if (gtk_tree_model_get_iter (model, &iter, path)) {
1581 ECalClient *client = NULL;
1582 ECalComponent *comp = NULL;
1584 gtk_tree_model_get (model, &iter,
1585 COLUMN_CAL_CLIENT, &client,
1586 COLUMN_CAL_COMPONENT, &comp,
1587 -1);
1589 if (client && comp) {
1590 e_cal_ops_open_component_in_editor_sync (NULL, client,
1591 e_cal_component_get_icalcomponent (comp), FALSE);
1594 g_clear_object (&client);
1595 g_clear_object (&comp);
1599 static void
1600 etdp_source_changed_cb (ESourceRegistry *registry,
1601 ESource *source,
1602 gpointer user_data)
1604 EToDoPane *to_do_pane = user_data;
1606 g_return_if_fail (E_IS_SOURCE (source));
1607 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1609 if (g_hash_table_contains (to_do_pane->priv->client_colors, source)) {
1610 ESourceSelectable *selectable = NULL;
1612 if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
1613 selectable = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
1614 else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
1615 selectable = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
1617 if (selectable) {
1618 GdkRGBA rgba;
1619 gchar *color_spec;
1621 color_spec = e_source_selectable_dup_color (selectable);
1622 if (color_spec && gdk_rgba_parse (&rgba, color_spec)) {
1623 GdkRGBA *current_rgba;
1625 current_rgba = g_hash_table_lookup (to_do_pane->priv->client_colors, source);
1626 if (!gdk_rgba_equal (current_rgba, &rgba)) {
1627 g_hash_table_insert (to_do_pane->priv->client_colors, source, gdk_rgba_copy (&rgba));
1628 etdp_update_colors (to_do_pane, FALSE);
1632 g_free (color_spec);
1637 static gboolean
1638 etdp_get_tree_view_selected_one (EToDoPane *to_do_pane,
1639 ECalClient **out_client,
1640 ECalComponent **out_comp)
1642 GtkTreeSelection *selection;
1643 GList *rows;
1644 GtkTreeIter iter;
1645 GtkTreeModel *model = NULL;
1646 gboolean had_any = FALSE;
1648 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
1650 if (out_client)
1651 *out_client = NULL;
1653 if (out_comp)
1654 *out_comp = NULL;
1656 selection = gtk_tree_view_get_selection (to_do_pane->priv->tree_view);
1657 rows = gtk_tree_selection_get_selected_rows (selection, &model);
1659 if (rows && gtk_tree_model_get_iter (model, &iter, rows->data)) {
1660 ECalClient *client = NULL;
1661 ECalComponent *comp = NULL;
1663 gtk_tree_model_get (model, &iter,
1664 COLUMN_CAL_CLIENT, &client,
1665 COLUMN_CAL_COMPONENT, &comp,
1666 -1);
1668 if (out_client && client)
1669 *out_client = g_object_ref (client);
1671 if (out_comp && comp)
1672 *out_comp = g_object_ref (comp);
1674 had_any = client || comp;
1676 g_clear_object (&client);
1677 g_clear_object (&comp);
1680 g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
1682 return had_any;
1685 static void
1686 etdp_new_common (EToDoPane *to_do_pane,
1687 ECalClientSourceType source_type,
1688 gboolean is_assigned)
1690 ECalClient *client = NULL;
1691 EShellView *shell_view;
1692 EShellWindow *shell_window;
1693 gchar *client_source_uid = NULL;
1695 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1697 if (etdp_get_tree_view_selected_one (to_do_pane, &client, NULL) && client) {
1698 ESource *source;
1700 source = e_client_get_source (E_CLIENT (client));
1701 if (source) {
1702 const gchar *extension_name = NULL;
1704 switch (source_type) {
1705 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1706 extension_name = E_SOURCE_EXTENSION_CALENDAR;
1707 break;
1708 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1709 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1710 break;
1711 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1712 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1713 break;
1714 default:
1715 break;
1718 /* Cannot ask to create an event in a task list or vice versa. */
1719 if (!extension_name || !e_source_has_extension (source, extension_name))
1720 source = NULL;
1723 if (source)
1724 client_source_uid = e_source_dup_uid (source);
1727 g_clear_object (&client);
1729 shell_view = e_to_do_pane_ref_shell_view (to_do_pane);
1730 shell_window = shell_view ? e_shell_view_get_shell_window (shell_view) : NULL;
1732 if (source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
1733 GSettings *settings;
1734 time_t dtstart = 0, dtend = 0;
1735 GtkTreeSelection *selection;
1736 GList *rows;
1737 GtkTreeIter iter;
1738 GtkTreeModel *model = NULL;
1740 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
1742 selection = gtk_tree_view_get_selection (to_do_pane->priv->tree_view);
1743 rows = gtk_tree_selection_get_selected_rows (selection, &model);
1745 if (rows && gtk_tree_model_get_iter (model, &iter, rows->data)) {
1746 GtkTreeIter parent;
1747 guint date_mark = 0;
1749 while (gtk_tree_model_iter_parent (model, &parent, &iter))
1750 iter = parent;
1752 gtk_tree_model_get (model, &iter, COLUMN_DATE_MARK, &date_mark, -1);
1754 if (date_mark > 0) {
1755 struct icaltimetype now;
1756 gint time_divisions_secs;
1757 icaltimezone *zone;
1759 time_divisions_secs = g_settings_get_int (settings, "time-divisions") * 60;
1760 zone = e_cal_data_model_get_timezone (to_do_pane->priv->events_data_model);
1761 now = icaltime_current_time_with_zone (zone);
1763 now.year = date_mark / 10000;
1764 now.month = (date_mark / 100) % 100;
1765 now.day = date_mark % 100;
1767 /* The date_mark is the next day, not the day it belongs to */
1768 icaltime_adjust (&now, -1, 0, 0, 0);
1770 dtstart = icaltime_as_timet_with_zone (now, zone);
1771 if (dtstart > 0 && time_divisions_secs > 0) {
1772 dtstart = dtstart + time_divisions_secs - (dtstart % time_divisions_secs);
1773 dtend = dtstart + time_divisions_secs;
1774 } else {
1775 dtstart = 0;
1780 g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
1782 e_cal_ops_new_event_editor (shell_window, client_source_uid, is_assigned, FALSE,
1783 g_settings_get_boolean (settings, "use-default-reminder"),
1784 g_settings_get_int (settings, "default-reminder-interval"),
1785 g_settings_get_enum (settings, "default-reminder-units"),
1786 dtstart, dtend);
1788 g_clear_object (&settings);
1789 } else {
1790 e_cal_ops_new_component_editor (shell_window, source_type, client_source_uid, is_assigned);
1793 g_clear_object (&shell_view);
1794 g_free (client_source_uid);
1797 static void
1798 etdp_new_appointment_cb (GtkMenuItem *item,
1799 gpointer user_data)
1801 EToDoPane *to_do_pane = user_data;
1803 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1805 etdp_new_common (to_do_pane, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, FALSE);
1808 static void
1809 etdp_new_meeting_cb (GtkMenuItem *item,
1810 gpointer user_data)
1812 EToDoPane *to_do_pane = user_data;
1814 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1816 etdp_new_common (to_do_pane, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, TRUE);
1819 static void
1820 etdp_new_task_cb (GtkMenuItem *item,
1821 gpointer user_data)
1823 EToDoPane *to_do_pane = user_data;
1825 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1827 etdp_new_common (to_do_pane, E_CAL_CLIENT_SOURCE_TYPE_TASKS, FALSE);
1830 static void
1831 etdp_new_assigned_task_cb (GtkMenuItem *item,
1832 gpointer user_data)
1834 EToDoPane *to_do_pane = user_data;
1836 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1838 etdp_new_common (to_do_pane, E_CAL_CLIENT_SOURCE_TYPE_TASKS, TRUE);
1841 static void
1842 etdp_open_selected_cb (GtkMenuItem *item,
1843 gpointer user_data)
1845 EToDoPane *to_do_pane = user_data;
1846 ECalClient *client = NULL;
1847 ECalComponent *comp = NULL;
1849 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1851 if (etdp_get_tree_view_selected_one (to_do_pane, &client, &comp) && client && comp) {
1852 e_cal_ops_open_component_in_editor_sync (NULL, client,
1853 e_cal_component_get_icalcomponent (comp), FALSE);
1856 g_clear_object (&client);
1857 g_clear_object (&comp);
1860 typedef struct _RemoveOperationData {
1861 ECalClient *client;
1862 gchar *uid;
1863 gchar *rid;
1864 ECalObjModType mod;
1865 } RemoveOperationData;
1867 static void
1868 remove_operation_data_free (gpointer ptr)
1870 RemoveOperationData *rod = ptr;
1872 if (rod) {
1873 g_clear_object (&rod->client);
1874 g_free (rod->uid);
1875 g_free (rod->rid);
1876 g_free (rod);
1880 static void
1881 etdp_remove_component_thread (EAlertSinkThreadJobData *job_data,
1882 gpointer user_data,
1883 GCancellable *cancellable,
1884 GError **error)
1886 RemoveOperationData *rod = user_data;
1888 g_return_if_fail (rod != NULL);
1890 e_cal_client_remove_object_sync (rod->client, rod->uid, rod->rid, rod->mod, cancellable, error);
1893 static void
1894 etdp_delete_common (EToDoPane *to_do_pane,
1895 ECalObjModType mod)
1897 ECalClient *client = NULL;
1898 ECalComponent *comp = NULL;
1900 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1902 if (etdp_get_tree_view_selected_one (to_do_pane, &client, &comp) && client && comp) {
1903 const gchar *description;
1904 const gchar *alert_ident;
1905 gchar *display_name;
1906 GCancellable *cancellable;
1907 ESource *source;
1908 RemoveOperationData *rod;
1909 ECalComponentId *id;
1911 id = e_cal_component_get_id (comp);
1912 g_return_if_fail (id != NULL);
1914 if (!e_cal_dialogs_delete_component (comp, FALSE, 1, e_cal_component_get_vtype (comp), GTK_WIDGET (to_do_pane))) {
1915 e_cal_component_free_id (id);
1916 g_clear_object (&client);
1917 g_clear_object (&comp);
1918 return;
1921 switch (e_cal_client_get_source_type (client)) {
1922 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1923 description = _("Removing an event");
1924 alert_ident = "calendar:failed-remove-event";
1925 break;
1926 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1927 description = _("Removing a memo");
1928 alert_ident = "calendar:failed-remove-memo";
1929 break;
1930 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1931 description = _("Removing a task");
1932 alert_ident = "calendar:failed-remove-task";
1933 break;
1934 default:
1935 g_warn_if_reached ();
1936 return;
1939 if (!e_cal_component_is_instance (comp))
1940 mod = E_CAL_OBJ_MOD_ALL;
1942 rod = g_new0 (RemoveOperationData,1);
1943 rod->client = g_object_ref (client);
1944 rod->uid = g_strdup (id->uid);
1945 rod->rid = g_strdup (id->rid);
1946 rod->mod = mod;
1948 source = e_client_get_source (E_CLIENT (client));
1949 display_name = e_util_get_source_full_name (e_source_registry_watcher_get_registry (to_do_pane->priv->watcher), source);
1951 /* It doesn't matter which data-model is picked, because it's used
1952 only for thread creation and manipulation, not for its content. */
1953 cancellable = e_cal_data_model_submit_thread_job (to_do_pane->priv->events_data_model, description, alert_ident,
1954 display_name, etdp_remove_component_thread, rod, remove_operation_data_free);
1956 e_cal_component_free_id (id);
1957 g_clear_object (&cancellable);
1958 g_free (display_name);
1961 g_clear_object (&client);
1962 g_clear_object (&comp);
1965 static void
1966 etdp_delete_selected_cb (GtkMenuItem *item,
1967 gpointer user_data)
1969 EToDoPane *to_do_pane = user_data;
1971 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1973 etdp_delete_common (to_do_pane, E_CAL_OBJ_MOD_THIS);
1976 static void
1977 etdp_delete_series_cb (GtkMenuItem *item,
1978 gpointer user_data)
1980 EToDoPane *to_do_pane = user_data;
1982 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1984 etdp_delete_common (to_do_pane, E_CAL_OBJ_MOD_ALL);
1987 static void
1988 etdp_show_tasks_without_due_date_cb (GtkCheckMenuItem *check_menu_item,
1989 gpointer user_data)
1991 EToDoPane *to_do_pane = user_data;
1993 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
1995 e_to_do_pane_set_show_no_duedate_tasks (to_do_pane, !e_to_do_pane_get_show_no_duedate_tasks (to_do_pane));
1998 static void
1999 etdp_fill_popup_menu (EToDoPane *to_do_pane,
2000 GtkMenu *menu)
2002 GtkWidget *item;
2003 GtkMenuShell *menu_shell;
2004 ECalClient *client = NULL;
2005 ECalComponent *comp = NULL;
2007 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2008 g_return_if_fail (GTK_IS_MENU (menu));
2010 etdp_get_tree_view_selected_one (to_do_pane, &client, &comp);
2012 menu_shell = GTK_MENU_SHELL (menu);
2014 item = gtk_image_menu_item_new_with_mnemonic (_("New _Appointment..."));
2015 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2016 gtk_image_new_from_icon_name ("appointment-new", GTK_ICON_SIZE_MENU));
2017 g_signal_connect (item, "activate",
2018 G_CALLBACK (etdp_new_appointment_cb), to_do_pane);
2019 gtk_widget_show (item);
2020 gtk_menu_shell_append (menu_shell, item);
2022 item = gtk_image_menu_item_new_with_mnemonic (_("New _Meeting..."));
2023 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2024 gtk_image_new_from_icon_name ("stock_people", GTK_ICON_SIZE_MENU));
2025 g_signal_connect (item, "activate",
2026 G_CALLBACK (etdp_new_meeting_cb), to_do_pane);
2027 gtk_widget_show (item);
2028 gtk_menu_shell_append (menu_shell, item);
2030 item = gtk_image_menu_item_new_with_mnemonic (_("New _Task..."));
2031 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2032 gtk_image_new_from_icon_name ("stock_task", GTK_ICON_SIZE_MENU));
2033 g_signal_connect (item, "activate",
2034 G_CALLBACK (etdp_new_task_cb), to_do_pane);
2035 gtk_widget_show (item);
2036 gtk_menu_shell_append (menu_shell, item);
2038 item = gtk_image_menu_item_new_with_mnemonic (_("_New Assigned Task..."));
2039 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2040 gtk_image_new_from_icon_name ("stock_task-assigned", GTK_ICON_SIZE_MENU));
2041 g_signal_connect (item, "activate",
2042 G_CALLBACK (etdp_new_assigned_task_cb), to_do_pane);
2043 gtk_widget_show (item);
2044 gtk_menu_shell_append (menu_shell, item);
2046 if (client && comp) {
2047 item = gtk_separator_menu_item_new ();
2048 gtk_widget_show (item);
2049 gtk_menu_shell_append (menu_shell, item);
2051 item = gtk_image_menu_item_new_with_mnemonic (_("_Open..."));
2052 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2053 gtk_image_new_from_icon_name ("document-open", GTK_ICON_SIZE_MENU));
2054 g_signal_connect (item, "activate",
2055 G_CALLBACK (etdp_open_selected_cb), to_do_pane);
2056 gtk_widget_show (item);
2057 gtk_menu_shell_append (menu_shell, item);
2059 item = gtk_separator_menu_item_new ();
2060 gtk_widget_show (item);
2061 gtk_menu_shell_append (menu_shell, item);
2063 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT &&
2064 e_cal_component_is_instance (comp)) {
2065 item = gtk_image_menu_item_new_with_mnemonic (_("_Delete This Instance..."));
2066 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2067 gtk_image_new_from_icon_name ("edit-delete", GTK_ICON_SIZE_MENU));
2068 g_signal_connect (item, "activate",
2069 G_CALLBACK (etdp_delete_selected_cb), to_do_pane);
2070 gtk_widget_show (item);
2071 gtk_menu_shell_append (menu_shell, item);
2073 item = gtk_image_menu_item_new_with_mnemonic (_("D_elete All Instances..."));
2074 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2075 gtk_image_new_from_icon_name ("edit-delete", GTK_ICON_SIZE_MENU));
2076 g_signal_connect (item, "activate",
2077 G_CALLBACK (etdp_delete_series_cb), to_do_pane);
2078 gtk_widget_show (item);
2079 gtk_menu_shell_append (menu_shell, item);
2080 } else {
2081 item = gtk_image_menu_item_new_with_mnemonic (_("_Delete..."));
2082 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
2083 gtk_image_new_from_icon_name ("edit-delete", GTK_ICON_SIZE_MENU));
2084 g_signal_connect (item, "activate",
2085 G_CALLBACK (etdp_delete_series_cb), to_do_pane);
2086 gtk_widget_show (item);
2087 gtk_menu_shell_append (menu_shell, item);
2091 g_clear_object (&client);
2092 g_clear_object (&comp);
2094 item = gtk_separator_menu_item_new ();
2095 gtk_widget_show (item);
2096 gtk_menu_shell_append (menu_shell, item);
2098 item = gtk_check_menu_item_new_with_mnemonic (_("_Show Tasks without Due date"));
2099 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), to_do_pane->priv->show_no_duedate_tasks);
2100 g_signal_connect (item, "toggled",
2101 G_CALLBACK (etdp_show_tasks_without_due_date_cb), to_do_pane);
2102 gtk_widget_show (item);
2103 gtk_menu_shell_append (menu_shell, item);
2106 static void
2107 etdp_popup_menu (EToDoPane *to_do_pane,
2108 GdkEvent *event)
2110 GtkMenu *menu;
2112 menu = GTK_MENU (gtk_menu_new ());
2114 etdp_fill_popup_menu (to_do_pane, menu);
2116 gtk_menu_attach_to_widget (menu, GTK_WIDGET (to_do_pane->priv->tree_view), NULL);
2117 g_signal_connect (menu, "deactivate", G_CALLBACK (gtk_menu_detach), NULL);
2118 gtk_menu_popup_at_pointer (menu, event);
2121 static gboolean
2122 etdp_button_press_event_cb (GtkWidget *widget,
2123 GdkEvent *event,
2124 gpointer user_data)
2126 EToDoPane *to_do_pane = user_data;
2128 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
2130 if (event->type == GDK_BUTTON_PRESS &&
2131 gdk_event_triggers_context_menu (event)) {
2132 GtkTreeSelection *selection;
2133 GtkTreePath *path;
2135 selection = gtk_tree_view_get_selection (to_do_pane->priv->tree_view);
2136 if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE)
2137 gtk_tree_selection_unselect_all (selection);
2139 if (gtk_tree_view_get_path_at_pos (to_do_pane->priv->tree_view, event->button.x, event->button.y, &path, NULL, NULL, NULL)) {
2140 gtk_tree_selection_select_path (selection, path);
2141 gtk_tree_view_set_cursor (to_do_pane->priv->tree_view, path, NULL, FALSE);
2143 gtk_tree_path_free (path);
2146 etdp_popup_menu (to_do_pane, event);
2148 return TRUE;
2151 return FALSE;
2154 static gboolean
2155 etdp_popup_menu_cb (GtkWidget *widget,
2156 gpointer user_data)
2158 EToDoPane *to_do_pane = user_data;
2160 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
2162 etdp_popup_menu (to_do_pane, NULL);
2164 return TRUE;
2167 static void
2168 etcp_notify_visible_cb (EToDoPane *to_do_pane,
2169 GParamSpec *param,
2170 gpointer user_data)
2172 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2174 if (gtk_widget_get_visible (GTK_WIDGET (to_do_pane))) {
2175 e_source_registry_watcher_reclaim (to_do_pane->priv->watcher);
2176 } else {
2177 GList *clients, *link;
2179 clients = e_cal_data_model_get_clients (to_do_pane->priv->events_data_model);
2180 for (link = clients; link; link = g_list_next (link)) {
2181 ECalClient *client = link->data;
2182 ESource *source = e_client_get_source (E_CLIENT (client));
2184 e_cal_data_model_remove_client (to_do_pane->priv->events_data_model, e_source_get_uid (source));
2186 g_list_free_full (clients, g_object_unref);
2188 clients = e_cal_data_model_get_clients (to_do_pane->priv->tasks_data_model);
2189 for (link = clients; link; link = g_list_next (link)) {
2190 ECalClient *client = link->data;
2191 ESource *source = e_client_get_source (E_CLIENT (client));
2193 e_cal_data_model_remove_client (to_do_pane->priv->tasks_data_model, e_source_get_uid (source));
2195 g_list_free_full (clients, g_object_unref);
2199 static void
2200 e_to_do_pane_set_shell_view (EToDoPane *to_do_pane,
2201 EShellView *shell_view)
2203 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2204 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
2206 g_weak_ref_set (&to_do_pane->priv->shell_view_weakref, shell_view);
2209 static void
2210 e_to_do_pane_set_property (GObject *object,
2211 guint property_id,
2212 const GValue *value,
2213 GParamSpec *pspec)
2215 switch (property_id) {
2216 case PROP_HIGHLIGHT_OVERDUE:
2217 e_to_do_pane_set_highlight_overdue (
2218 E_TO_DO_PANE (object),
2219 g_value_get_boolean (value));
2220 return;
2222 case PROP_OVERDUE_COLOR:
2223 e_to_do_pane_set_overdue_color (
2224 E_TO_DO_PANE (object),
2225 g_value_get_boxed (value));
2226 return;
2228 case PROP_SHELL_VIEW:
2229 e_to_do_pane_set_shell_view (
2230 E_TO_DO_PANE (object),
2231 g_value_get_object (value));
2232 return;
2234 case PROP_SHOW_COMPLETED_TASKS:
2235 e_to_do_pane_set_show_completed_tasks (
2236 E_TO_DO_PANE (object),
2237 g_value_get_boolean (value));
2238 return;
2240 case PROP_SHOW_NO_DUEDATE_TASKS:
2241 e_to_do_pane_set_show_no_duedate_tasks (
2242 E_TO_DO_PANE (object),
2243 g_value_get_boolean (value));
2244 return;
2246 case PROP_USE_24HOUR_FORMAT:
2247 e_to_do_pane_set_use_24hour_format (
2248 E_TO_DO_PANE (object),
2249 g_value_get_boolean (value));
2250 return;
2253 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2256 static void
2257 e_to_do_pane_get_property (GObject *object,
2258 guint property_id,
2259 GValue *value,
2260 GParamSpec *pspec)
2262 switch (property_id) {
2263 case PROP_HIGHLIGHT_OVERDUE:
2264 g_value_set_boolean (
2265 value,
2266 e_to_do_pane_get_highlight_overdue (
2267 E_TO_DO_PANE (object)));
2268 return;
2270 case PROP_OVERDUE_COLOR:
2271 g_value_set_boxed (
2272 value,
2273 e_to_do_pane_get_overdue_color (
2274 E_TO_DO_PANE (object)));
2275 return;
2277 case PROP_SHELL_VIEW:
2278 g_value_set_object (
2279 value,
2280 e_to_do_pane_ref_shell_view (
2281 E_TO_DO_PANE (object)));
2282 return;
2284 case PROP_SHOW_COMPLETED_TASKS:
2285 g_value_set_boolean (
2286 value,
2287 e_to_do_pane_get_show_completed_tasks (
2288 E_TO_DO_PANE (object)));
2289 return;
2291 case PROP_SHOW_NO_DUEDATE_TASKS:
2292 g_value_set_boolean (
2293 value,
2294 e_to_do_pane_get_show_no_duedate_tasks (
2295 E_TO_DO_PANE (object)));
2296 return;
2298 case PROP_USE_24HOUR_FORMAT:
2299 g_value_set_boolean (
2300 value,
2301 e_to_do_pane_get_use_24hour_format (
2302 E_TO_DO_PANE (object)));
2303 return;
2306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2309 static void
2310 e_to_do_pane_constructed (GObject *object)
2312 EToDoPane *to_do_pane = E_TO_DO_PANE (object);
2313 EShellView *shell_view;
2314 EShell *shell;
2315 GtkGrid *grid;
2316 GtkWidget *widget;
2317 GtkCellRenderer *renderer;
2318 GtkTreeView *tree_view;
2319 GtkTreeViewColumn *column;
2320 GtkTreeModel *model, *sort_model;
2321 GtkTreeIter iter;
2322 GSettings *settings;
2323 PangoAttrList *bold;
2324 gint ii;
2326 /* Chain up to parent's method. */
2327 G_OBJECT_CLASS (e_to_do_pane_parent_class)->constructed (object);
2329 shell_view = e_to_do_pane_ref_shell_view (to_do_pane);
2330 shell = e_shell_backend_get_shell (e_shell_view_get_shell_backend (shell_view));
2332 to_do_pane->priv->client_cache = g_object_ref (e_shell_get_client_cache (shell));
2333 to_do_pane->priv->watcher = e_source_registry_watcher_new (e_shell_get_registry (shell), NULL);
2334 to_do_pane->priv->source_changed_id =
2335 g_signal_connect (e_source_registry_watcher_get_registry (to_do_pane->priv->watcher), "source-changed",
2336 G_CALLBACK (etdp_source_changed_cb), to_do_pane);
2338 g_signal_connect (to_do_pane->priv->watcher, "filter",
2339 G_CALLBACK (e_to_do_pane_watcher_filter_cb), NULL);
2341 g_signal_connect (to_do_pane->priv->watcher, "appeared",
2342 G_CALLBACK (e_to_do_pane_watcher_appeared_cb), to_do_pane);
2344 g_signal_connect (to_do_pane->priv->watcher, "disappeared",
2345 G_CALLBACK (e_to_do_pane_watcher_disappeared_cb), to_do_pane);
2347 to_do_pane->priv->tree_store = GTK_TREE_STORE (gtk_tree_store_new (N_COLUMNS,
2348 GDK_TYPE_RGBA, /* COLUMN_BGCOLOR */
2349 GDK_TYPE_RGBA, /* COLUMN_FGCOLOR */
2350 G_TYPE_BOOLEAN, /* COLUMN_HAS_ICON_NAME */
2351 G_TYPE_STRING, /* COLUMN_ICON_NAME */
2352 G_TYPE_STRING, /* COLUMN_SUMMARY */
2353 G_TYPE_STRING, /* COLUMN_TOOLTIP */
2354 G_TYPE_STRING, /* COLUMN_SORTKEY */
2355 G_TYPE_UINT, /* COLUMN_DATE_MARK */
2356 E_TYPE_CAL_CLIENT, /* COLUMN_CAL_CLIENT */
2357 E_TYPE_CAL_COMPONENT)); /* COLUMN_CAL_COMPONENT */
2359 grid = GTK_GRID (to_do_pane);
2361 bold = pango_attr_list_new ();
2362 pango_attr_list_insert (bold, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
2364 widget = gtk_label_new (_("To Do"));
2365 g_object_set (G_OBJECT (widget),
2366 "halign", GTK_ALIGN_CENTER,
2367 "hexpand", TRUE,
2368 "valign", GTK_ALIGN_START,
2369 "vexpand", FALSE,
2370 "attributes", bold,
2371 NULL);
2372 gtk_grid_attach (grid, widget, 0, 0, 1, 1);
2374 pango_attr_list_unref (bold);
2376 model = GTK_TREE_MODEL (to_do_pane->priv->tree_store);
2378 sort_model = gtk_tree_model_sort_new_with_model (model);
2379 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), COLUMN_SORTKEY, GTK_SORT_ASCENDING);
2381 widget = gtk_tree_view_new_with_model (sort_model);
2383 g_object_set (G_OBJECT (widget),
2384 "halign", GTK_ALIGN_FILL,
2385 "hexpand", TRUE,
2386 "valign", GTK_ALIGN_FILL,
2387 "vexpand", TRUE,
2388 NULL);
2390 tree_view = GTK_TREE_VIEW (widget);
2392 widget = gtk_scrolled_window_new (NULL, NULL);
2393 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2394 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (tree_view));
2396 g_object_set (G_OBJECT (widget),
2397 "halign", GTK_ALIGN_FILL,
2398 "hexpand", TRUE,
2399 "valign", GTK_ALIGN_FILL,
2400 "vexpand", TRUE,
2401 NULL);
2403 gtk_grid_attach (grid, widget, 0, 1, 1, 1);
2405 column = gtk_tree_view_column_new ();
2407 renderer = gtk_cell_renderer_pixbuf_new ();
2409 gtk_tree_view_column_pack_start (column, renderer, FALSE);
2411 gtk_tree_view_column_set_attributes (column, renderer,
2412 "icon-name", COLUMN_ICON_NAME,
2413 "visible", COLUMN_HAS_ICON_NAME,
2414 NULL);
2416 renderer = gtk_cell_renderer_text_new ();
2418 g_object_set (G_OBJECT (renderer),
2419 "ellipsize", PANGO_ELLIPSIZE_END,
2420 NULL);
2422 gtk_tree_view_column_pack_start (column, renderer, TRUE);
2424 gtk_tree_view_column_set_attributes (column, renderer,
2425 "markup", COLUMN_SUMMARY,
2426 "background-rgba", COLUMN_BGCOLOR,
2427 "foreground-rgba", COLUMN_FGCOLOR,
2428 NULL);
2430 gtk_tree_view_append_column (tree_view, column);
2431 gtk_tree_view_set_expander_column (tree_view, column);
2433 for (ii = 0; ii < N_ROOTS - 1; ii++) {
2434 GtkTreePath *path;
2435 gchar *sort_key;
2437 sort_key = g_strdup_printf ("%c", 'A' + ii);
2439 gtk_tree_store_append (to_do_pane->priv->tree_store, &iter, NULL);
2440 gtk_tree_store_set (to_do_pane->priv->tree_store, &iter,
2441 COLUMN_SORTKEY, sort_key,
2442 COLUMN_HAS_ICON_NAME, FALSE,
2443 -1);
2445 g_free (sort_key);
2447 path = gtk_tree_model_get_path (model, &iter);
2449 to_do_pane->priv->roots[ii] = gtk_tree_row_reference_new (model, path);
2450 g_warn_if_fail (to_do_pane->priv->roots[ii] != NULL);
2452 gtk_tree_path_free (path);
2455 gtk_tree_view_set_headers_visible (tree_view, FALSE);
2456 gtk_tree_view_set_tooltip_column (tree_view, COLUMN_TOOLTIP);
2458 gtk_widget_show_all (GTK_WIDGET (grid));
2460 to_do_pane->priv->events_data_model = e_cal_data_model_new (e_to_do_pane_submit_thread_job, G_OBJECT (to_do_pane));
2461 to_do_pane->priv->tasks_data_model = e_cal_data_model_new (e_to_do_pane_submit_thread_job, G_OBJECT (to_do_pane));
2462 to_do_pane->priv->time_checker_id = g_timeout_add_seconds (60, etdp_check_time_cb, to_do_pane);
2464 e_cal_data_model_set_expand_recurrences (to_do_pane->priv->events_data_model, TRUE);
2465 e_cal_data_model_set_expand_recurrences (to_do_pane->priv->tasks_data_model, FALSE);
2467 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
2469 g_settings_bind_with_mapping (
2470 settings, "timezone",
2471 to_do_pane->priv->events_data_model, "timezone",
2472 G_SETTINGS_BIND_GET,
2473 etdp_settings_map_string_to_icaltimezone,
2474 NULL, /* one-way binding */
2475 NULL, NULL);
2477 g_settings_bind_with_mapping (
2478 settings, "timezone",
2479 to_do_pane->priv->tasks_data_model, "timezone",
2480 G_SETTINGS_BIND_GET,
2481 etdp_settings_map_string_to_icaltimezone,
2482 NULL, /* one-way binding */
2483 NULL, NULL);
2485 g_settings_bind (
2486 settings, "task-overdue-highlight",
2487 to_do_pane, "highlight-overdue",
2488 G_SETTINGS_BIND_GET);
2490 g_settings_bind_with_mapping (
2491 settings, "task-overdue-color",
2492 to_do_pane, "overdue-color",
2493 G_SETTINGS_BIND_GET,
2494 etdp_settings_map_string_to_rgba,
2495 NULL, /* one-way binding */
2496 NULL, NULL);
2498 g_settings_bind (
2499 settings, "use-24hour-format",
2500 to_do_pane, "use-24hour-format",
2501 G_SETTINGS_BIND_GET);
2503 g_object_unref (settings);
2505 g_signal_connect (to_do_pane->priv->events_data_model, "notify::timezone",
2506 G_CALLBACK (etdp_timezone_changed_cb), to_do_pane);
2508 g_signal_connect (tree_view, "row-activated",
2509 G_CALLBACK (etdp_row_activated_cb), to_do_pane);
2511 g_signal_connect (tree_view, "button-press-event",
2512 G_CALLBACK (etdp_button_press_event_cb), to_do_pane);
2514 g_signal_connect (tree_view, "popup-menu",
2515 G_CALLBACK (etdp_popup_menu_cb), to_do_pane);
2517 to_do_pane->priv->tree_view = tree_view;
2519 etdp_check_time_changed (to_do_pane, TRUE);
2521 g_clear_object (&shell_view);
2522 g_clear_object (&sort_model);
2524 g_signal_connect (to_do_pane, "notify::visible",
2525 G_CALLBACK (etcp_notify_visible_cb), NULL);
2527 if (gtk_widget_get_visible (GTK_WIDGET (to_do_pane)))
2528 e_source_registry_watcher_reclaim (to_do_pane->priv->watcher);
2531 static void
2532 e_to_do_pane_dispose (GObject *object)
2534 EToDoPane *to_do_pane = E_TO_DO_PANE (object);
2535 gint ii;
2537 if (to_do_pane->priv->cancellable) {
2538 g_cancellable_cancel (to_do_pane->priv->cancellable);
2539 g_clear_object (&to_do_pane->priv->cancellable);
2542 if (to_do_pane->priv->time_checker_id) {
2543 g_source_remove (to_do_pane->priv->time_checker_id);
2544 to_do_pane->priv->time_checker_id = 0;
2547 if (to_do_pane->priv->source_changed_id) {
2548 g_signal_handler_disconnect (e_source_registry_watcher_get_registry (to_do_pane->priv->watcher),
2549 to_do_pane->priv->source_changed_id);
2550 to_do_pane->priv->source_changed_id = 0;
2553 for (ii = 0; ii < N_ROOTS; ii++) {
2554 gtk_tree_row_reference_free (to_do_pane->priv->roots[ii]);
2555 to_do_pane->priv->roots[ii] = NULL;
2558 g_hash_table_remove_all (to_do_pane->priv->component_refs);
2559 g_hash_table_remove_all (to_do_pane->priv->client_colors);
2561 g_clear_object (&to_do_pane->priv->client_cache);
2562 g_clear_object (&to_do_pane->priv->watcher);
2563 g_clear_object (&to_do_pane->priv->tree_store);
2564 g_clear_object (&to_do_pane->priv->events_data_model);
2565 g_clear_object (&to_do_pane->priv->tasks_data_model);
2567 g_weak_ref_set (&to_do_pane->priv->shell_view_weakref, NULL);
2569 /* Chain up to parent's method. */
2570 G_OBJECT_CLASS (e_to_do_pane_parent_class)->dispose (object);
2573 static void
2574 e_to_do_pane_finalize (GObject *object)
2576 EToDoPane *to_do_pane = E_TO_DO_PANE (object);
2578 g_weak_ref_clear (&to_do_pane->priv->shell_view_weakref);
2580 g_hash_table_destroy (to_do_pane->priv->component_refs);
2581 g_hash_table_destroy (to_do_pane->priv->client_colors);
2583 if (to_do_pane->priv->overdue_color)
2584 gdk_rgba_free (to_do_pane->priv->overdue_color);
2586 /* Chain up to parent's method. */
2587 G_OBJECT_CLASS (e_to_do_pane_parent_class)->finalize (object);
2590 static void
2591 e_to_do_pane_init (EToDoPane *to_do_pane)
2593 to_do_pane->priv = G_TYPE_INSTANCE_GET_PRIVATE (to_do_pane, E_TYPE_TO_DO_PANE, EToDoPanePrivate);
2594 to_do_pane->priv->cancellable = g_cancellable_new ();
2596 to_do_pane->priv->component_refs = g_hash_table_new_full (component_ident_hash, component_ident_equal,
2597 component_ident_free, etdp_free_component_refs);
2599 to_do_pane->priv->client_colors = g_hash_table_new_full (g_direct_hash, g_direct_equal,
2600 NULL, (GDestroyNotify) gdk_rgba_free);
2602 to_do_pane->priv->nearest_due = (time_t) -1;
2604 g_weak_ref_init (&to_do_pane->priv->shell_view_weakref, NULL);
2607 static void
2608 e_to_do_pane_class_init (EToDoPaneClass *klass)
2610 GObjectClass *object_class;
2612 g_type_class_add_private (klass, sizeof (EToDoPanePrivate));
2614 object_class = G_OBJECT_CLASS (klass);
2615 object_class->set_property = e_to_do_pane_set_property;
2616 object_class->get_property = e_to_do_pane_get_property;
2617 object_class->constructed = e_to_do_pane_constructed;
2618 object_class->dispose = e_to_do_pane_dispose;
2619 object_class->finalize = e_to_do_pane_finalize;
2621 g_object_class_install_property (
2622 object_class,
2623 PROP_HIGHLIGHT_OVERDUE,
2624 g_param_spec_boolean (
2625 "highlight-overdue",
2626 "Highlight Overdue Tasks",
2627 NULL,
2628 FALSE,
2629 G_PARAM_READWRITE |
2630 G_PARAM_CONSTRUCT |
2631 G_PARAM_STATIC_STRINGS));
2633 g_object_class_install_property (
2634 object_class,
2635 PROP_OVERDUE_COLOR,
2636 g_param_spec_boxed (
2637 "overdue-color",
2638 "Overdue Color",
2639 NULL,
2640 GDK_TYPE_RGBA,
2641 G_PARAM_READWRITE |
2642 G_PARAM_STATIC_STRINGS));
2644 g_object_class_install_property (
2645 object_class,
2646 PROP_SHELL_VIEW,
2647 g_param_spec_object (
2648 "shell-view",
2649 "EShellView",
2650 NULL,
2651 E_TYPE_SHELL_VIEW,
2652 G_PARAM_READWRITE |
2653 G_PARAM_CONSTRUCT_ONLY |
2654 G_PARAM_STATIC_STRINGS));
2656 g_object_class_install_property (
2657 object_class,
2658 PROP_SHOW_COMPLETED_TASKS,
2659 g_param_spec_boolean (
2660 "show-completed-tasks",
2661 "Show Completed Tasks",
2662 NULL,
2663 FALSE,
2664 G_PARAM_READWRITE |
2665 G_PARAM_CONSTRUCT |
2666 G_PARAM_STATIC_STRINGS));
2668 g_object_class_install_property (
2669 object_class,
2670 PROP_SHOW_NO_DUEDATE_TASKS,
2671 g_param_spec_boolean (
2672 "show-no-duedate-tasks",
2673 "Show tasks without Due date",
2674 NULL,
2675 FALSE,
2676 G_PARAM_READWRITE |
2677 G_PARAM_CONSTRUCT |
2678 G_PARAM_STATIC_STRINGS));
2680 g_object_class_install_property (
2681 object_class,
2682 PROP_USE_24HOUR_FORMAT,
2683 g_param_spec_boolean (
2684 "use-24hour-format",
2685 "Use 24hour Format",
2686 NULL,
2687 FALSE,
2688 G_PARAM_READWRITE |
2689 G_PARAM_CONSTRUCT |
2690 G_PARAM_STATIC_STRINGS));
2693 static void
2694 e_to_do_pane_cal_data_model_subscriber_init (ECalDataModelSubscriberInterface *iface)
2696 iface->component_added = etdp_data_subscriber_component_added;
2697 iface->component_modified = etdp_data_subscriber_component_modified;
2698 iface->component_removed = etdp_data_subscriber_component_removed;
2699 iface->freeze = etdp_data_subscriber_freeze;
2700 iface->thaw = etdp_data_subscriber_thaw;
2704 * e_to_do_pane_new:
2705 * @shell_view: an #EShellView
2707 * Creates a new #EToDoPane.
2709 * Returns: (transfer full): A new #EToDoPane.
2711 * Since: 3.26
2713 GtkWidget *
2714 e_to_do_pane_new (EShellView *shell_view)
2716 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
2718 return g_object_new (E_TYPE_TO_DO_PANE,
2719 "shell-view", shell_view,
2720 NULL);
2724 * e_to_do_pane_ref_shell_view:
2725 * @to_do_pane: an #EToDoPane
2727 * Returns: (transfer full): an #EShellView used to create the @to_do_pane with added reference.
2728 * Free it with g_object_unref() when no longer needed.
2730 * Since: 3.26
2732 EShellView *
2733 e_to_do_pane_ref_shell_view (EToDoPane *to_do_pane)
2735 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), NULL);
2737 return g_weak_ref_get (&to_do_pane->priv->shell_view_weakref);
2741 * e_to_do_pane_get_highlight_overdue:
2742 * @to_do_pane: an #EToDoPane
2744 * Returns: Whether highlights overdue tasks with overdue-color.
2746 * Since: 3.26
2748 gboolean
2749 e_to_do_pane_get_highlight_overdue (EToDoPane *to_do_pane)
2751 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
2753 return to_do_pane->priv->highlight_overdue;
2757 * e_to_do_pane_set_highlight_overdue:
2758 * @to_do_pane: an #EToDoPane
2759 * @highlight_overdue: a value to set
2761 * Sets whether should highlight overdue tasks with overdue-color.
2763 * Since: 3.26
2765 void
2766 e_to_do_pane_set_highlight_overdue (EToDoPane *to_do_pane,
2767 gboolean highlight_overdue)
2769 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2771 if ((to_do_pane->priv->highlight_overdue ? 1 : 0) == (highlight_overdue ? 1 : 0))
2772 return;
2774 to_do_pane->priv->highlight_overdue = highlight_overdue;
2776 if (to_do_pane->priv->overdue_color)
2777 etdp_update_colors (to_do_pane, TRUE);
2779 g_object_notify (G_OBJECT (to_do_pane), "highlight-overdue");
2783 * e_to_do_pane_get_overdue_color:
2784 * @to_do_pane: an #EToDoPane
2786 * Returns: (transfer none) (nullable): Currently set color to use for overdue tasks.
2788 * Since: 3.26
2790 const GdkRGBA *
2791 e_to_do_pane_get_overdue_color (EToDoPane *to_do_pane)
2793 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), NULL);
2795 return to_do_pane->priv->overdue_color;
2799 * e_to_do_pane_set_overdue_color:
2800 * @to_do_pane: an #EToDoPane
2801 * @overdue_color: (nullable): a color to set, or %NULL
2803 * Sets a color to use for overdue tasks, or unsets the previous,
2804 * when it's %NULL.
2806 * Since: 3.26
2808 void
2809 e_to_do_pane_set_overdue_color (EToDoPane *to_do_pane,
2810 const GdkRGBA *overdue_color)
2812 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2814 if (to_do_pane->priv->overdue_color == overdue_color ||
2815 (to_do_pane->priv->overdue_color && overdue_color &&
2816 gdk_rgba_equal (to_do_pane->priv->overdue_color, overdue_color)))
2817 return;
2819 if (to_do_pane->priv->overdue_color) {
2820 gdk_rgba_free (to_do_pane->priv->overdue_color);
2821 to_do_pane->priv->overdue_color = NULL;
2824 if (overdue_color)
2825 to_do_pane->priv->overdue_color = gdk_rgba_copy (overdue_color);
2827 if (to_do_pane->priv->highlight_overdue)
2828 etdp_update_colors (to_do_pane, TRUE);
2830 g_object_notify (G_OBJECT (to_do_pane), "overdue-color");
2834 * e_to_do_pane_get_show_completed_tasks:
2835 * @to_do_pane: an #EToDoPane
2837 * Returns: Whether completed tasks should be shown in the view.
2839 * Since: 3.26
2841 gboolean
2842 e_to_do_pane_get_show_completed_tasks (EToDoPane *to_do_pane)
2844 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
2846 return to_do_pane->priv->show_completed_tasks;
2850 * e_to_do_pane_set_show_completed_tasks:
2851 * @to_do_pane: an #EToDoPane
2852 * @show_completed_tasks: a value to set
2854 * Sets whether completed tasks should be shown in the view.
2856 * Since: 3.26
2858 void
2859 e_to_do_pane_set_show_completed_tasks (EToDoPane *to_do_pane,
2860 gboolean show_completed_tasks)
2862 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2864 if ((to_do_pane->priv->show_completed_tasks ? 1 : 0) == (show_completed_tasks ? 1 : 0))
2865 return;
2867 to_do_pane->priv->show_completed_tasks = show_completed_tasks;
2869 etdp_update_queries (to_do_pane);
2871 g_object_notify (G_OBJECT (to_do_pane), "show-completed-tasks");
2875 * e_to_do_pane_get_show_no_duedate_tasks:
2876 * @to_do_pane: an #EToDoPane
2878 * Returns: Whether tasks without Due date should be shown in the view.
2880 * Since: 3.28
2882 gboolean
2883 e_to_do_pane_get_show_no_duedate_tasks (EToDoPane *to_do_pane)
2885 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
2887 return to_do_pane->priv->show_no_duedate_tasks;
2891 * e_to_do_pane_set_show_no_duedate_tasks:
2892 * @to_do_pane: an #EToDoPane
2893 * @show_no_duedate_tasks: a value to set
2895 * Sets whether tasks without Due date should be shown in the view.
2897 * Since: 3.28
2899 void
2900 e_to_do_pane_set_show_no_duedate_tasks (EToDoPane *to_do_pane,
2901 gboolean show_no_duedate_tasks)
2903 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2905 if ((to_do_pane->priv->show_no_duedate_tasks ? 1 : 0) == (show_no_duedate_tasks ? 1 : 0))
2906 return;
2908 to_do_pane->priv->show_no_duedate_tasks = show_no_duedate_tasks;
2910 etdp_update_queries (to_do_pane);
2912 g_object_notify (G_OBJECT (to_do_pane), "show-no-duedate-tasks");
2916 * e_to_do_pane_get_use_24hour_format:
2917 * @to_do_pane: an #EToDoPane
2919 * Returns: Whether uses 24-hour format for time display.
2921 * Since: 3.26
2923 gboolean
2924 e_to_do_pane_get_use_24hour_format (EToDoPane *to_do_pane)
2926 g_return_val_if_fail (E_IS_TO_DO_PANE (to_do_pane), FALSE);
2928 return to_do_pane->priv->use_24hour_format;
2932 * e_to_do_pane_set_use_24hour_format:
2933 * @to_do_pane: an #EToDoPane
2934 * @use_24hour_format: a value to set
2936 * Sets whether to use 24-hour format for time display.
2938 * Since: 3.26
2940 void
2941 e_to_do_pane_set_use_24hour_format (EToDoPane *to_do_pane,
2942 gboolean use_24hour_format)
2944 g_return_if_fail (E_IS_TO_DO_PANE (to_do_pane));
2946 if ((to_do_pane->priv->use_24hour_format ? 1 : 0) == (use_24hour_format ? 1 : 0))
2947 return;
2949 to_do_pane->priv->use_24hour_format = use_24hour_format;
2951 etdp_update_all (to_do_pane);
2953 g_object_notify (G_OBJECT (to_do_pane), "use-24hour-format");