Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-task-table.c
blobc442f95a4b8e61aaf561fe596e3b9d7ac579e255
1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 * Authors:
16 * Damon Chaplin <damon@ximian.com>
17 * Rodrigo Moya <rodrigo@ximian.com>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 * ETaskTable - displays the ECalComponent objects in a table (an ETable).
27 #include "evolution-config.h"
29 #include "e-task-table.h"
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <glib/gi18n.h>
34 #include <glib/gstdio.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkkeysyms.h>
38 #include "calendar-config.h"
39 #include "comp-util.h"
40 #include "e-cal-dialogs.h"
41 #include "e-cal-model-tasks.h"
42 #include "e-cal-ops.h"
43 #include "e-calendar-view.h"
44 #include "e-cell-date-edit-text.h"
45 #include "itip-utils.h"
46 #include "print.h"
47 #include "misc.h"
49 #define E_TASK_TABLE_GET_PRIVATE(obj) \
50 (G_TYPE_INSTANCE_GET_PRIVATE \
51 ((obj), E_TYPE_TASK_TABLE, ETaskTablePrivate))
53 struct _ETaskTablePrivate {
54 gpointer shell_view; /* weak pointer */
55 ECalModel *model;
56 GCancellable *completed_cancellable; /* when processing completed tasks */
58 GtkTargetList *copy_target_list;
59 GtkTargetList *paste_target_list;
61 gulong notify_highlight_due_today_id;
62 gulong notify_color_due_today_id;
63 gulong notify_highlight_overdue_id;
64 gulong notify_color_overdue_id;
67 enum {
68 PROP_0,
69 PROP_COPY_TARGET_LIST,
70 PROP_MODEL,
71 PROP_PASTE_TARGET_LIST,
72 PROP_SHELL_VIEW
75 enum {
76 OPEN_COMPONENT,
77 POPUP_EVENT,
78 LAST_SIGNAL
81 static struct tm e_task_table_get_current_time (ECellDateEdit *ecde, gpointer data);
83 static guint signals[LAST_SIGNAL];
85 /* The icons to represent the task. */
86 static const gchar *icon_names[] = {
87 "stock_task",
88 "stock_task-recurring",
89 "stock_task-assigned",
90 "stock_task-assigned-to"
93 /* Forward Declarations */
94 static void e_task_table_selectable_init
95 (ESelectableInterface *iface);
97 G_DEFINE_TYPE_WITH_CODE (
98 ETaskTable,
99 e_task_table,
100 E_TYPE_TABLE,
101 G_IMPLEMENT_INTERFACE (
102 E_TYPE_SELECTABLE,
103 e_task_table_selectable_init))
105 static void
106 task_table_emit_open_component (ETaskTable *task_table,
107 ECalModelComponent *comp_data)
109 guint signal_id = signals[OPEN_COMPONENT];
111 g_signal_emit (task_table, signal_id, 0, comp_data);
114 static void
115 task_table_emit_popup_event (ETaskTable *task_table,
116 GdkEvent *event)
118 g_signal_emit (task_table, signals[POPUP_EVENT], 0, event);
121 static gint
122 task_table_percent_compare_cb (gconstpointer a,
123 gconstpointer b,
124 gpointer cmp_cache)
126 gint percent1 = GPOINTER_TO_INT (a);
127 gint percent2 = GPOINTER_TO_INT (b);
129 return (percent1 < percent2) ? -1 : (percent1 > percent2);
132 static gint
133 task_table_priority_compare_cb (gconstpointer a,
134 gconstpointer b,
135 gpointer cmp_cache)
137 gint priority1, priority2;
139 priority1 = e_cal_util_priority_from_string ((const gchar *) a);
140 priority2 = e_cal_util_priority_from_string ((const gchar *) b);
142 /* We change undefined priorities so they appear after 'Low'. */
143 if (priority1 <= 0)
144 priority1 = 10;
145 if (priority2 <= 0)
146 priority2 = 10;
148 /* We'll just use the ordering of the priority values. */
149 return (priority1 < priority2) ? -1 : (priority1 > priority2);
152 static const gchar *
153 get_cache_str (gpointer cmp_cache,
154 const gchar *str)
156 const gchar *value;
158 if (!cmp_cache || !str)
159 return str;
161 value = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, str);
162 if (!value) {
163 gchar *ckey;
165 ckey = g_utf8_collate_key (str, -1);
166 e_table_sorting_utils_add_to_cmp_cache (cmp_cache, (gchar *) str, ckey);
167 value = ckey;
170 return value;
173 static gboolean
174 same_cache_string (gpointer cmp_cache,
175 const gchar *str_a,
176 const gchar *str_b)
178 if (!cmp_cache)
179 return g_utf8_collate (str_a, str_b) == 0;
181 str_b = get_cache_str (cmp_cache, str_b);
183 g_return_val_if_fail (str_a != NULL, FALSE);
184 g_return_val_if_fail (str_b != NULL, FALSE);
186 return strcmp (str_a, str_b) == 0;
189 static gint
190 task_table_status_compare_cb (gconstpointer a,
191 gconstpointer b,
192 gpointer cmp_cache)
194 const gchar *string_a = a;
195 const gchar *string_b = b;
196 gint status_a = -2;
197 gint status_b = -2;
199 if (string_a == NULL || *string_a == '\0')
200 status_a = -1;
201 else {
202 const gchar *cache_str = get_cache_str (cmp_cache, string_a);
204 if (same_cache_string (cmp_cache, cache_str, _("Not Started")))
205 status_a = 0;
206 else if (same_cache_string (cmp_cache, cache_str, _("In Progress")))
207 status_a = 1;
208 else if (same_cache_string (cmp_cache, cache_str, _("Completed")))
209 status_a = 2;
210 else if (same_cache_string (cmp_cache, cache_str, _("Cancelled")))
211 status_a = 3;
214 if (string_b == NULL || *string_b == '\0')
215 status_b = -1;
216 else {
217 const gchar *cache_str = get_cache_str (cmp_cache, string_b);
219 if (same_cache_string (cmp_cache, cache_str, _("Not Started")))
220 status_b = 0;
221 else if (same_cache_string (cmp_cache, cache_str, _("In Progress")))
222 status_b = 1;
223 else if (same_cache_string (cmp_cache, cache_str, _("Completed")))
224 status_b = 2;
225 else if (same_cache_string (cmp_cache, cache_str, _("Cancelled")))
226 status_b = 3;
229 return (status_a < status_b) ? -1 : (status_a > status_b);
232 /* Deletes all of the selected components in the table */
233 static void
234 delete_selected_components (ETaskTable *task_table)
236 GSList *objs;
238 objs = e_task_table_get_selected (task_table);
239 e_cal_ops_delete_ecalmodel_components (task_table->priv->model, objs);
240 g_slist_free (objs);
243 static void
244 task_table_queue_draw_cb (ECalModelTasks *tasks_model,
245 GParamSpec *param,
246 GtkWidget *task_table)
248 g_return_if_fail (task_table != NULL);
250 gtk_widget_queue_draw (task_table);
253 static void
254 task_table_dates_cell_before_popup_cb (ECellDateEdit *dates_cell,
255 gint row,
256 gint view_col,
257 gpointer user_data)
259 ECalModel *model;
260 ECalModelComponent *comp_data;
261 ETaskTable *task_table = user_data;
262 ESelectionModel *esm;
263 gboolean date_only;
265 g_return_if_fail (E_IS_TASK_TABLE (task_table));
267 esm = e_table_get_selection_model (E_TABLE (task_table));
268 if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
269 row = e_sorter_sorted_to_model (esm->sorter, row);
271 model = e_task_table_get_model (task_table);
272 comp_data = e_cal_model_get_component_at (model, row);
273 date_only = comp_data && comp_data->client && e_client_check_capability (E_CLIENT (comp_data->client), CAL_STATIC_CAPABILITY_TASK_DATE_ONLY);
275 g_object_set (G_OBJECT (dates_cell), "show-time", !date_only, NULL);
278 static void
279 task_table_set_model (ETaskTable *task_table,
280 ECalModel *model)
282 g_return_if_fail (task_table->priv->model == NULL);
284 task_table->priv->model = g_object_ref (model);
286 /* redraw on drawing options change */
287 task_table->priv->notify_highlight_due_today_id = e_signal_connect_notify (
288 model, "notify::highlight-due-today",
289 G_CALLBACK (task_table_queue_draw_cb),
290 task_table);
292 task_table->priv->notify_color_due_today_id = e_signal_connect_notify (
293 model, "notify::color-due-today",
294 G_CALLBACK (task_table_queue_draw_cb),
295 task_table);
297 task_table->priv->notify_highlight_overdue_id = e_signal_connect_notify (
298 model, "notify::highlight-overdue",
299 G_CALLBACK (task_table_queue_draw_cb),
300 task_table);
302 task_table->priv->notify_color_overdue_id = e_signal_connect_notify (
303 model, "notify::color-overdue",
304 G_CALLBACK (task_table_queue_draw_cb),
305 task_table);
308 static void
309 task_table_set_shell_view (ETaskTable *task_table,
310 EShellView *shell_view)
312 g_return_if_fail (task_table->priv->shell_view == NULL);
314 task_table->priv->shell_view = shell_view;
316 g_object_add_weak_pointer (
317 G_OBJECT (shell_view),
318 &task_table->priv->shell_view);
321 static void
322 task_table_set_property (GObject *object,
323 guint property_id,
324 const GValue *value,
325 GParamSpec *pspec)
327 switch (property_id) {
328 case PROP_MODEL:
329 task_table_set_model (
330 E_TASK_TABLE (object),
331 g_value_get_object (value));
332 return;
334 case PROP_SHELL_VIEW:
335 task_table_set_shell_view (
336 E_TASK_TABLE (object),
337 g_value_get_object (value));
338 return;
341 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
344 static void
345 task_table_get_property (GObject *object,
346 guint property_id,
347 GValue *value,
348 GParamSpec *pspec)
350 switch (property_id) {
351 case PROP_COPY_TARGET_LIST:
352 g_value_set_boxed (
353 value, e_task_table_get_copy_target_list (
354 E_TASK_TABLE (object)));
355 return;
357 case PROP_MODEL:
358 g_value_set_object (
359 value, e_task_table_get_model (
360 E_TASK_TABLE (object)));
361 return;
363 case PROP_PASTE_TARGET_LIST:
364 g_value_set_boxed (
365 value, e_task_table_get_paste_target_list (
366 E_TASK_TABLE (object)));
367 return;
369 case PROP_SHELL_VIEW:
370 g_value_set_object (
371 value, e_task_table_get_shell_view (
372 E_TASK_TABLE (object)));
373 return;
376 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
379 static void
380 task_table_dispose (GObject *object)
382 ETaskTablePrivate *priv;
384 priv = E_TASK_TABLE_GET_PRIVATE (object);
386 if (priv->completed_cancellable) {
387 g_cancellable_cancel (priv->completed_cancellable);
388 g_object_unref (priv->completed_cancellable);
389 priv->completed_cancellable = NULL;
392 if (priv->shell_view != NULL) {
393 g_object_remove_weak_pointer (
394 G_OBJECT (priv->shell_view), &priv->shell_view);
395 priv->shell_view = NULL;
398 if (priv->model != NULL) {
399 g_signal_handlers_disconnect_by_data (priv->model, object);
401 e_signal_disconnect_notify_handler (priv->model, &priv->notify_highlight_due_today_id);
402 e_signal_disconnect_notify_handler (priv->model, &priv->notify_color_due_today_id);
403 e_signal_disconnect_notify_handler (priv->model, &priv->notify_highlight_overdue_id);
404 e_signal_disconnect_notify_handler (priv->model, &priv->notify_color_overdue_id);
406 g_object_unref (priv->model);
407 priv->model = NULL;
410 if (priv->copy_target_list != NULL) {
411 gtk_target_list_unref (priv->copy_target_list);
412 priv->copy_target_list = NULL;
415 if (priv->paste_target_list != NULL) {
416 gtk_target_list_unref (priv->paste_target_list);
417 priv->paste_target_list = NULL;
420 /* Chain up to parent's dispose() method. */
421 G_OBJECT_CLASS (e_task_table_parent_class)->dispose (object);
424 static void
425 task_table_constructed (GObject *object)
427 ETaskTable *task_table;
428 ECalModel *model;
429 ECell *cell, *popup_cell;
430 ETableExtras *extras;
431 ETableSpecification *specification;
432 GList *strings;
433 AtkObject *a11y;
434 gchar *etspecfile;
435 gint percent;
436 GError *local_error = NULL;
438 task_table = E_TASK_TABLE (object);
439 model = e_task_table_get_model (task_table);
441 /* Create the header columns */
443 extras = e_table_extras_new ();
446 * Normal string fields.
448 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
449 g_object_set (
450 cell,
451 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
452 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
453 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
454 NULL);
456 e_table_extras_add_cell (extras, "calstring", cell);
457 g_object_unref (cell);
460 * Date fields.
462 cell = e_cell_date_edit_text_new (NULL, GTK_JUSTIFY_LEFT);
463 g_object_set (
464 cell,
465 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
466 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
467 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
468 NULL);
470 e_binding_bind_property (
471 model, "timezone",
472 cell, "timezone",
473 G_BINDING_BIDIRECTIONAL |
474 G_BINDING_SYNC_CREATE);
476 e_binding_bind_property (
477 model, "use-24-hour-format",
478 cell, "use-24-hour-format",
479 G_BINDING_BIDIRECTIONAL |
480 G_BINDING_SYNC_CREATE);
482 popup_cell = e_cell_date_edit_new ();
483 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
484 g_object_unref (cell);
486 e_binding_bind_property (
487 model, "use-24-hour-format",
488 popup_cell, "use-24-hour-format",
489 G_BINDING_BIDIRECTIONAL |
490 G_BINDING_SYNC_CREATE);
492 e_table_extras_add_cell (extras, "dateedit", popup_cell);
493 g_object_unref (popup_cell);
495 task_table->dates_cell = E_CELL_DATE_EDIT (popup_cell);
497 g_signal_connect (task_table->dates_cell, "before-popup",
498 G_CALLBACK (task_table_dates_cell_before_popup_cb), task_table);
500 e_cell_date_edit_set_get_time_callback (
501 E_CELL_DATE_EDIT (popup_cell),
502 e_task_table_get_current_time, task_table, NULL);
505 * Combo fields.
508 /* Classification field. */
509 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
510 g_object_set (
511 cell,
512 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
513 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
514 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
515 "editable", FALSE,
516 NULL);
518 popup_cell = e_cell_combo_new ();
519 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
520 g_object_unref (cell);
522 strings = NULL;
523 strings = g_list_append (strings, (gchar *) _("Public"));
524 strings = g_list_append (strings, (gchar *) _("Private"));
525 strings = g_list_append (strings, (gchar *) _("Confidential"));
526 e_cell_combo_set_popdown_strings (
527 E_CELL_COMBO (popup_cell),
528 strings);
529 g_list_free (strings);
531 e_table_extras_add_cell (extras, "classification", popup_cell);
532 g_object_unref (popup_cell);
534 /* Priority field. */
535 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
536 g_object_set (
537 cell,
538 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
539 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
540 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
541 "editable", FALSE,
542 NULL);
544 popup_cell = e_cell_combo_new ();
545 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
546 g_object_unref (cell);
548 strings = NULL;
549 strings = g_list_append (strings, (gchar *) _("High"));
550 strings = g_list_append (strings, (gchar *) _("Normal"));
551 strings = g_list_append (strings, (gchar *) _("Low"));
552 strings = g_list_append (strings, (gchar *) _("Undefined"));
553 e_cell_combo_set_popdown_strings (
554 E_CELL_COMBO (popup_cell),
555 strings);
556 g_list_free (strings);
558 e_table_extras_add_cell (extras, "priority", popup_cell);
559 g_object_unref (popup_cell);
561 /* Percent field. */
562 cell = e_cell_percent_new (NULL, GTK_JUSTIFY_LEFT);
563 g_object_set (
564 cell,
565 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
566 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
567 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
568 NULL);
570 popup_cell = e_cell_combo_new ();
571 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
572 g_object_unref (cell);
574 strings = NULL;
575 for (percent = 0; percent <= 100; percent += 10) {
576 /* Translators: "%d%%" is the percentage of a task done.
577 * %d is the actual value, %% is replaced with a percent sign.
578 * Result values will be 0%, 10%, 20%, ... 100%
580 strings = g_list_append (strings, g_strdup_printf (_("%d%%"), percent));
582 e_cell_combo_set_popdown_strings (
583 E_CELL_COMBO (popup_cell),
584 strings);
586 g_list_foreach (strings, (GFunc) g_free, NULL);
587 g_list_free (strings);
589 e_table_extras_add_cell (extras, "percent", popup_cell);
590 g_object_unref (popup_cell);
592 /* Transparency field. */
593 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
594 g_object_set (
595 cell,
596 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
597 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
598 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
599 "editable", FALSE,
600 NULL);
602 popup_cell = e_cell_combo_new ();
603 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
604 g_object_unref (cell);
606 strings = NULL;
607 strings = g_list_append (strings, (gchar *) _("Free"));
608 strings = g_list_append (strings, (gchar *) _("Busy"));
609 e_cell_combo_set_popdown_strings (
610 E_CELL_COMBO (popup_cell),
611 strings);
612 g_list_free (strings);
614 e_table_extras_add_cell (extras, "transparency", popup_cell);
615 g_object_unref (popup_cell);
617 /* Status field. */
618 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
619 g_object_set (
620 cell,
621 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
622 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
623 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
624 "editable", FALSE,
625 NULL);
627 popup_cell = e_cell_combo_new ();
628 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
629 g_object_unref (cell);
631 strings = NULL;
632 strings = g_list_append (strings, (gchar *) _("Not Started"));
633 strings = g_list_append (strings, (gchar *) _("In Progress"));
634 strings = g_list_append (strings, (gchar *) _("Completed"));
635 strings = g_list_append (strings, (gchar *) _("Cancelled"));
636 e_cell_combo_set_popdown_strings (
637 E_CELL_COMBO (popup_cell),
638 strings);
639 g_list_free (strings);
641 e_table_extras_add_cell (extras, "calstatus", popup_cell);
642 g_object_unref (popup_cell);
644 e_table_extras_add_compare (
645 extras, "date-compare",
646 e_cell_date_edit_compare_cb);
647 e_table_extras_add_compare (
648 extras, "percent-compare",
649 task_table_percent_compare_cb);
650 e_table_extras_add_compare (
651 extras, "priority-compare",
652 task_table_priority_compare_cb);
653 e_table_extras_add_compare (
654 extras, "status-compare",
655 task_table_status_compare_cb);
657 /* Create pixmaps */
659 cell = e_cell_toggle_new (icon_names, G_N_ELEMENTS (icon_names));
660 e_table_extras_add_cell (extras, "icon", cell);
661 g_object_unref (cell);
663 e_table_extras_add_icon_name (extras, "icon", "stock_task");
665 e_table_extras_add_icon_name (extras, "complete", "stock_check-filled");
667 /* set proper format component for a default 'date' cell renderer */
668 cell = e_table_extras_get_cell (extras, "date");
669 e_cell_date_set_format_component (E_CELL_DATE (cell), "calendar");
671 /* Create the table */
673 etspecfile = g_build_filename (
674 EVOLUTION_ETSPECDIR, "e-task-table.etspec", NULL);
675 specification = e_table_specification_new (etspecfile, &local_error);
677 /* Failure here is fatal. */
678 if (local_error != NULL) {
679 g_error ("%s: %s", etspecfile, local_error->message);
680 g_return_if_reached ();
683 e_table_construct (
684 E_TABLE (task_table),
685 E_TABLE_MODEL (model),
686 extras, specification);
688 g_object_unref (specification);
689 g_free (etspecfile);
691 gtk_widget_set_has_tooltip (GTK_WIDGET (task_table), TRUE);
693 g_object_unref (extras);
695 a11y = gtk_widget_get_accessible (GTK_WIDGET (task_table));
696 if (a11y)
697 atk_object_set_name (a11y, _("Tasks"));
699 /* Chain up to parent's constructed() method. */
700 G_OBJECT_CLASS (e_task_table_parent_class)->constructed (object);
703 static gboolean
704 task_table_popup_menu (GtkWidget *widget)
706 ETaskTable *task_table;
708 task_table = E_TASK_TABLE (widget);
709 task_table_emit_popup_event (task_table, NULL);
711 return TRUE;
714 static gboolean
715 task_table_query_tooltip (GtkWidget *widget,
716 gint x,
717 gint y,
718 gboolean keyboard_mode,
719 GtkTooltip *tooltip)
721 ETaskTable *task_table;
722 ECalModel *model;
723 ECalModelComponent *comp_data;
724 gint row = -1, col = -1, row_y = -1, row_height = -1;
725 GtkWidget *box, *l, *w;
726 GdkRGBA sel_bg, sel_fg, norm_bg, norm_text;
727 gchar *tmp;
728 const gchar *str;
729 GString *tmp2;
730 gboolean free_text = FALSE;
731 ECalComponent *new_comp;
732 ECalComponentOrganizer organizer;
733 ECalComponentDateTime dtstart, dtdue;
734 icalcomponent *clone;
735 icaltimezone *zone, *default_zone;
736 GSList *desc, *p;
737 gint len;
738 ESelectionModel *esm;
739 struct tm tmp_tm;
741 if (keyboard_mode)
742 return FALSE;
744 task_table = E_TASK_TABLE (widget);
746 e_table_get_mouse_over_cell (E_TABLE (task_table), &row, &col);
747 if (row == -1)
748 return FALSE;
750 /* Respect sorting option; the 'e_table_get_mouse_over_cell'
751 * returns sorted row, not the model one. */
752 esm = e_table_get_selection_model (E_TABLE (task_table));
753 if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
754 row = e_sorter_sorted_to_model (esm->sorter, row);
756 model = e_task_table_get_model (task_table);
757 comp_data = e_cal_model_get_component_at (model, row);
759 if (!comp_data || !comp_data->icalcomp)
760 return FALSE;
762 new_comp = e_cal_component_new ();
763 clone = icalcomponent_new_clone (comp_data->icalcomp);
764 if (!e_cal_component_set_icalcomponent (new_comp, clone)) {
765 g_object_unref (new_comp);
766 return FALSE;
769 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &sel_bg);
770 e_utils_get_theme_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, &sel_fg);
771 e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &norm_bg);
772 e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &norm_text);
774 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
776 str = e_calendar_view_get_icalcomponent_summary (
777 comp_data->client, comp_data->icalcomp, &free_text);
778 if (!(str && *str)) {
779 if (free_text)
780 g_free ((gchar *) str);
781 free_text = FALSE;
782 str = _("* No Summary *");
785 l = gtk_label_new (NULL);
786 tmp = g_markup_printf_escaped ("<b>%s</b>", str);
787 gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
788 gtk_label_set_markup (GTK_LABEL (l), tmp);
789 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
790 w = gtk_event_box_new ();
792 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &sel_bg);
793 gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &sel_fg);
794 gtk_container_add (GTK_CONTAINER (w), l);
795 gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
796 g_free (tmp);
798 if (free_text)
799 g_free ((gchar *) str);
800 free_text = FALSE;
802 w = gtk_event_box_new ();
803 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &norm_bg);
805 l = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
806 gtk_container_add (GTK_CONTAINER (w), l);
807 gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
808 w = l;
810 e_cal_component_get_organizer (new_comp, &organizer);
811 if (organizer.cn) {
812 gchar *ptr;
813 ptr = strchr (organizer.value, ':');
815 if (ptr) {
816 ptr++;
817 /* To Translators: It will display
818 * "Organizer: NameOfTheUser <email@ofuser.com>" */
819 tmp = g_strdup_printf (_("Organizer: %s <%s>"), organizer.cn, ptr);
820 } else {
821 /* With SunOne accounts, there may be no ':' in
822 * organizer.value. */
823 tmp = g_strdup_printf (_("Organizer: %s"), organizer.cn);
826 l = gtk_label_new (tmp);
827 gtk_label_set_line_wrap (GTK_LABEL (l), FALSE);
828 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
829 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
830 g_free (tmp);
832 gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
835 e_cal_component_get_dtstart (new_comp, &dtstart);
836 e_cal_component_get_due (new_comp, &dtdue);
838 default_zone = e_cal_model_get_timezone (model);
840 if (dtstart.tzid) {
841 zone = icalcomponent_get_timezone (
842 e_cal_component_get_icalcomponent (new_comp),
843 dtstart.tzid);
844 if (!zone)
845 e_cal_client_get_timezone_sync (
846 comp_data->client, dtstart.tzid, &zone, NULL, NULL);
847 if (!zone)
848 zone = default_zone;
849 } else {
850 zone = NULL;
853 tmp2 = g_string_new ("");
855 if (dtstart.value) {
856 gchar *str;
858 tmp_tm = icaltimetype_to_tm_with_zone (dtstart.value, zone, default_zone);
859 str = e_datetime_format_format_tm ("calendar", "table",
860 dtstart.value->is_date ? DTFormatKindDate : DTFormatKindDateTime,
861 &tmp_tm);
863 if (str && *str) {
864 g_string_append (tmp2, _("Start: "));
865 g_string_append (tmp2, str);
868 g_free (str);
871 if (dtdue.value) {
872 gchar *str;
874 tmp_tm = icaltimetype_to_tm_with_zone (dtdue.value, zone, default_zone);
875 str = e_datetime_format_format_tm ("calendar", "table",
876 dtdue.value->is_date ? DTFormatKindDate : DTFormatKindDateTime,
877 &tmp_tm);
879 if (str && *str) {
880 if (tmp2->len)
881 g_string_append (tmp2, "; ");
883 g_string_append (tmp2, _("Due: "));
884 g_string_append (tmp2, str);
887 g_free (str);
890 if (tmp2->len) {
891 l = gtk_label_new (tmp2->str);
892 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
893 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
895 gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
898 g_string_free (tmp2, TRUE);
900 e_cal_component_free_datetime (&dtstart);
901 e_cal_component_free_datetime (&dtdue);
903 tmp = e_cal_model_get_attendees_status_info (
904 model, new_comp, comp_data->client);
905 if (tmp) {
906 l = gtk_label_new (tmp);
907 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
908 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
910 g_free (tmp);
911 tmp = NULL;
913 gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
916 tmp = cal_comp_util_get_attendee_comments (e_cal_component_get_icalcomponent (new_comp));
917 if (tmp) {
918 l = gtk_label_new (tmp);
919 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
920 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
921 gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
923 g_free (tmp);
924 tmp = NULL;
927 tmp2 = g_string_new ("");
928 e_cal_component_get_description_list (new_comp, &desc);
929 for (len = 0, p = desc; p != NULL; p = p->next) {
930 ECalComponentText *text = p->data;
932 if (text->value != NULL) {
933 len += strlen (text->value);
934 g_string_append (tmp2, text->value);
935 if (len > 1024) {
936 g_string_set_size (tmp2, 1020);
937 g_string_append (tmp2, "...");
938 break;
942 e_cal_component_free_text_list (desc);
944 if (tmp2->len) {
945 l = gtk_label_new (tmp2->str);
946 gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
947 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
948 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
950 gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
953 g_string_free (tmp2, TRUE);
955 gtk_widget_show_all (box);
956 gtk_tooltip_set_custom (tooltip, box);
958 g_object_unref (new_comp);
960 if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
961 row = e_sorter_model_to_sorted (esm->sorter, row);
963 e_table_get_cell_geometry (E_TABLE (task_table), row, 0, NULL, &row_y, NULL, &row_height);
965 if (row_y != -1 && row_height != -1) {
966 ETable *etable;
967 GdkRectangle rect;
968 GtkAllocation allocation;
970 etable = E_TABLE (task_table);
972 if (etable && etable->table_canvas) {
973 gtk_widget_get_allocation (GTK_WIDGET (etable->table_canvas), &allocation);
974 } else {
975 allocation.x = 0;
976 allocation.y = 0;
977 allocation.width = 0;
978 allocation.height = 0;
981 rect.x = allocation.x;
982 rect.y = allocation.y + row_y - BUTTON_PADDING;
983 rect.width = allocation.width;
984 rect.height = row_height + 2 * BUTTON_PADDING;
986 if (etable && etable->header_canvas) {
987 gtk_widget_get_allocation (GTK_WIDGET (etable->header_canvas), &allocation);
989 rect.y += allocation.height;
992 gtk_tooltip_set_tip_area (tooltip, &rect);
995 return TRUE;
998 static void
999 task_table_double_click (ETable *table,
1000 gint row,
1001 gint col,
1002 GdkEvent *event)
1004 ETaskTable *task_table;
1005 ECalModel *model;
1006 ECalModelComponent *comp_data;
1008 task_table = E_TASK_TABLE (table);
1009 model = e_task_table_get_model (task_table);
1010 comp_data = e_cal_model_get_component_at (model, row);
1011 task_table_emit_open_component (task_table, comp_data);
1014 static gint
1015 task_table_right_click (ETable *table,
1016 gint row,
1017 gint col,
1018 GdkEvent *event)
1020 ETaskTable *task_table;
1022 task_table = E_TASK_TABLE (table);
1023 task_table_emit_popup_event (task_table, event);
1025 return TRUE;
1028 static gboolean
1029 task_table_white_space_event (ETable *table,
1030 GdkEvent *event)
1032 guint event_button = 0;
1034 g_return_val_if_fail (E_IS_TASK_TABLE (table), FALSE);
1035 g_return_val_if_fail (event != NULL, FALSE);
1037 if (event->type == GDK_BUTTON_PRESS &&
1038 gdk_event_get_button (event, &event_button) &&
1039 event_button == 3) {
1040 GtkWidget *table_canvas;
1042 table_canvas = GTK_WIDGET (table->table_canvas);
1044 if (!gtk_widget_has_focus (table_canvas))
1045 gtk_widget_grab_focus (table_canvas);
1047 task_table_emit_popup_event (E_TASK_TABLE (table), event);
1049 return TRUE;
1052 return FALSE;
1055 static void
1056 task_table_update_actions (ESelectable *selectable,
1057 EFocusTracker *focus_tracker,
1058 GdkAtom *clipboard_targets,
1059 gint n_clipboard_targets)
1061 ETaskTable *task_table;
1062 GtkAction *action;
1063 GtkTargetList *target_list;
1064 GSList *list, *iter;
1065 gboolean can_paste = FALSE;
1066 gboolean sources_are_editable = TRUE;
1067 gboolean is_editing;
1068 gboolean sensitive;
1069 const gchar *tooltip;
1070 gint n_selected;
1071 gint ii;
1073 task_table = E_TASK_TABLE (selectable);
1074 n_selected = e_table_selected_count (E_TABLE (task_table));
1075 is_editing = e_table_is_editing (E_TABLE (task_table));
1077 list = e_task_table_get_selected (task_table);
1078 for (iter = list; iter != NULL && sources_are_editable; iter = iter->next) {
1079 ECalModelComponent *comp_data = iter->data;
1081 if (!comp_data)
1082 continue;
1084 sources_are_editable = sources_are_editable &&
1085 !e_client_is_readonly (E_CLIENT (comp_data->client));
1087 g_slist_free (list);
1089 target_list = e_selectable_get_paste_target_list (selectable);
1090 for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
1091 can_paste = gtk_target_list_find (
1092 target_list, clipboard_targets[ii], NULL);
1094 action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
1095 sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
1096 tooltip = _("Cut selected tasks to the clipboard");
1097 gtk_action_set_sensitive (action, sensitive);
1098 gtk_action_set_tooltip (action, tooltip);
1100 action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
1101 sensitive = (n_selected > 0) && !is_editing;
1102 tooltip = _("Copy selected tasks to the clipboard");
1103 gtk_action_set_sensitive (action, sensitive);
1104 gtk_action_set_tooltip (action, tooltip);
1106 action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
1107 sensitive = sources_are_editable && can_paste && !is_editing;
1108 tooltip = _("Paste tasks from the clipboard");
1109 gtk_action_set_sensitive (action, sensitive);
1110 gtk_action_set_tooltip (action, tooltip);
1112 action = e_focus_tracker_get_delete_selection_action (focus_tracker);
1113 sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
1114 tooltip = _("Delete selected tasks");
1115 gtk_action_set_sensitive (action, sensitive);
1116 gtk_action_set_tooltip (action, tooltip);
1118 action = e_focus_tracker_get_select_all_action (focus_tracker);
1119 sensitive = TRUE;
1120 tooltip = _("Select all visible tasks");
1121 gtk_action_set_sensitive (action, sensitive);
1122 gtk_action_set_tooltip (action, tooltip);
1125 static void
1126 task_table_cut_clipboard (ESelectable *selectable)
1128 ETaskTable *task_table;
1130 task_table = E_TASK_TABLE (selectable);
1132 e_selectable_copy_clipboard (selectable);
1133 delete_selected_components (task_table);
1136 /* Helper for task_table_copy_clipboard() */
1137 static void
1138 copy_row_cb (gint model_row,
1139 gpointer data)
1141 ETaskTable *task_table;
1142 ECalModelComponent *comp_data;
1143 ECalModel *model;
1144 gchar *comp_str;
1145 icalcomponent *child;
1147 task_table = E_TASK_TABLE (data);
1149 g_return_if_fail (task_table->tmp_vcal != NULL);
1151 model = e_task_table_get_model (task_table);
1152 comp_data = e_cal_model_get_component_at (model, model_row);
1153 if (!comp_data)
1154 return;
1156 /* Add timezones to the VCALENDAR component. */
1157 e_cal_util_add_timezones_from_component (
1158 task_table->tmp_vcal, comp_data->icalcomp);
1160 /* Add the new component to the VCALENDAR component. */
1161 comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
1162 child = icalparser_parse_string (comp_str);
1163 if (child) {
1164 icalcomponent_add_component (
1165 task_table->tmp_vcal,
1166 icalcomponent_new_clone (child));
1167 icalcomponent_free (child);
1169 g_free (comp_str);
1172 static void
1173 task_table_copy_clipboard (ESelectable *selectable)
1175 ETaskTable *task_table;
1176 GtkClipboard *clipboard;
1177 gchar *comp_str;
1179 task_table = E_TASK_TABLE (selectable);
1181 /* Create a temporary VCALENDAR object. */
1182 task_table->tmp_vcal = e_cal_util_new_top_level ();
1184 e_table_selected_row_foreach (
1185 E_TABLE (task_table), copy_row_cb, task_table);
1186 comp_str = icalcomponent_as_ical_string_r (task_table->tmp_vcal);
1188 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1189 e_clipboard_set_calendar (clipboard, comp_str, -1);
1190 gtk_clipboard_store (clipboard);
1192 g_free (comp_str);
1194 icalcomponent_free (task_table->tmp_vcal);
1195 task_table->tmp_vcal = NULL;
1198 /* Helper for calenable_table_paste_clipboard() */
1199 static void
1200 clipboard_get_calendar_data (ETaskTable *task_table,
1201 const gchar *text)
1203 g_return_if_fail (E_IS_TASK_TABLE (task_table));
1205 if (!text || !*text)
1206 return;
1208 e_cal_ops_paste_components (e_task_table_get_model (task_table), text);
1211 static void
1212 task_table_paste_clipboard (ESelectable *selectable)
1214 ETaskTable *task_table;
1215 GtkClipboard *clipboard;
1216 GnomeCanvasItem *item;
1217 GnomeCanvas *table_canvas;
1219 task_table = E_TASK_TABLE (selectable);
1221 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1223 table_canvas = E_TABLE (task_table)->table_canvas;
1224 item = table_canvas->focused_item;
1226 /* XXX Should ECellText implement GtkEditable? */
1228 /* Paste text into a cell being edited. */
1229 if (gtk_clipboard_wait_is_text_available (clipboard) &&
1230 gtk_widget_has_focus (GTK_WIDGET (table_canvas)) &&
1231 E_IS_TABLE_ITEM (item) &&
1232 E_TABLE_ITEM (item)->editing_col >= 0 &&
1233 E_TABLE_ITEM (item)->editing_row >= 0) {
1235 ETableItem *etable_item = E_TABLE_ITEM (item);
1237 e_cell_text_paste_clipboard (
1238 etable_item->cell_views[etable_item->editing_col],
1239 etable_item->editing_col,
1240 etable_item->editing_row);
1242 /* Paste iCalendar data into the table. */
1243 } else if (e_clipboard_wait_is_calendar_available (clipboard)) {
1244 gchar *calendar_source;
1246 calendar_source = e_clipboard_wait_for_calendar (clipboard);
1247 clipboard_get_calendar_data (task_table, calendar_source);
1248 g_free (calendar_source);
1252 /* Used from e_table_selected_row_foreach(); puts the selected row number in an
1253 * gint pointed to by the closure data.
1255 static void
1256 get_selected_row_cb (gint model_row,
1257 gpointer data)
1259 gint *row;
1261 row = data;
1262 *row = model_row;
1266 * Returns the component that is selected in the table; only works if there is
1267 * one and only one selected row.
1269 static ECalModelComponent *
1270 get_selected_comp (ETaskTable *task_table)
1272 ECalModel *model;
1273 gint row;
1275 model = e_task_table_get_model (task_table);
1276 if (e_table_selected_count (E_TABLE (task_table)) != 1)
1277 return NULL;
1279 row = -1;
1280 e_table_selected_row_foreach (
1281 E_TABLE (task_table), get_selected_row_cb, &row);
1282 g_return_val_if_fail (row != -1, NULL);
1284 return e_cal_model_get_component_at (model, row);
1287 static void
1288 add_retract_data (ECalComponent *comp,
1289 const gchar *retract_comment)
1291 icalcomponent *icalcomp = NULL;
1292 icalproperty *icalprop = NULL;
1294 icalcomp = e_cal_component_get_icalcomponent (comp);
1295 if (retract_comment && *retract_comment)
1296 icalprop = icalproperty_new_x (retract_comment);
1297 else
1298 icalprop = icalproperty_new_x ("0");
1299 icalproperty_set_x_name (icalprop, "X-EVOLUTION-RETRACT-COMMENT");
1300 icalcomponent_add_property (icalcomp, icalprop);
1303 static gboolean
1304 check_for_retract (ECalComponent *comp,
1305 ECalClient *client)
1307 ECalComponentOrganizer org;
1308 gchar *email = NULL;
1309 const gchar *strip = NULL;
1310 gboolean ret_val;
1312 if (!e_cal_component_has_attendees (comp))
1313 return FALSE;
1315 if (!e_cal_client_check_save_schedules (client))
1316 return FALSE;
1318 e_cal_component_get_organizer (comp, &org);
1319 strip = itip_strip_mailto (org.value);
1321 ret_val = e_client_get_backend_property_sync (
1322 E_CLIENT (client),
1323 CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
1324 &email, NULL, NULL) && email != NULL &&
1325 g_ascii_strcasecmp (email, strip) == 0;
1327 g_free (email);
1329 return ret_val;
1332 static void
1333 task_table_delete_selection (ESelectable *selectable)
1335 ECalModel *model;
1336 ETaskTable *task_table;
1337 ECalModelComponent *comp_data;
1338 ECalComponent *comp = NULL;
1339 gboolean delete = TRUE;
1340 gint n_selected;
1342 task_table = E_TASK_TABLE (selectable);
1343 model = e_task_table_get_model (task_table);
1345 n_selected = e_table_selected_count (E_TABLE (task_table));
1346 if (n_selected <= 0)
1347 return;
1349 if (n_selected == 1)
1350 comp_data = get_selected_comp (task_table);
1351 else
1352 comp_data = NULL;
1354 /* FIXME: this may be something other than a TODO component */
1356 if (comp_data) {
1357 comp = e_cal_component_new ();
1358 e_cal_component_set_icalcomponent (
1359 comp, icalcomponent_new_clone (comp_data->icalcomp));
1362 if ((n_selected == 1) && comp && check_for_retract (comp, comp_data->client)) {
1363 gchar *retract_comment = NULL;
1364 gboolean retract = FALSE;
1366 delete = e_cal_dialogs_prompt_retract (GTK_WIDGET (task_table), comp, &retract_comment, &retract);
1367 if (retract) {
1368 icalcomponent *icalcomp = NULL;
1370 add_retract_data (comp, retract_comment);
1371 icalcomp = e_cal_component_get_icalcomponent (comp);
1372 icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL);
1374 e_cal_ops_send_component (model, comp_data->client, icalcomp);
1377 g_free (retract_comment);
1378 } else if (e_cal_model_get_confirm_delete (model))
1379 delete = e_cal_dialogs_delete_component (
1380 comp, FALSE, n_selected,
1381 E_CAL_COMPONENT_TODO,
1382 GTK_WIDGET (task_table));
1384 if (delete)
1385 delete_selected_components (task_table);
1387 /* free memory */
1388 if (comp)
1389 g_object_unref (comp);
1392 static void
1393 task_table_select_all (ESelectable *selectable)
1395 e_table_select_all (E_TABLE (selectable));
1398 static void
1399 e_task_table_class_init (ETaskTableClass *class)
1401 GObjectClass *object_class;
1402 GtkWidgetClass *widget_class;
1403 ETableClass *table_class;
1405 g_type_class_add_private (class, sizeof (ETaskTablePrivate));
1407 object_class = G_OBJECT_CLASS (class);
1408 object_class->set_property = task_table_set_property;
1409 object_class->get_property = task_table_get_property;
1410 object_class->dispose = task_table_dispose;
1411 object_class->constructed = task_table_constructed;
1413 widget_class = GTK_WIDGET_CLASS (class);
1414 widget_class->popup_menu = task_table_popup_menu;
1415 widget_class->query_tooltip = task_table_query_tooltip;
1417 table_class = E_TABLE_CLASS (class);
1418 table_class->double_click = task_table_double_click;
1419 table_class->right_click = task_table_right_click;
1420 table_class->white_space_event = task_table_white_space_event;
1422 /* Inherited from ESelectableInterface */
1423 g_object_class_override_property (
1424 object_class,
1425 PROP_COPY_TARGET_LIST,
1426 "copy-target-list");
1428 g_object_class_install_property (
1429 object_class,
1430 PROP_MODEL,
1431 g_param_spec_object (
1432 "model",
1433 "Model",
1434 NULL,
1435 E_TYPE_CAL_MODEL,
1436 G_PARAM_READWRITE |
1437 G_PARAM_CONSTRUCT_ONLY));
1439 /* Inherited from ESelectableInterface */
1440 g_object_class_override_property (
1441 object_class,
1442 PROP_PASTE_TARGET_LIST,
1443 "paste-target-list");
1445 g_object_class_install_property (
1446 object_class,
1447 PROP_SHELL_VIEW,
1448 g_param_spec_object (
1449 "shell-view",
1450 "Shell View",
1451 NULL,
1452 E_TYPE_SHELL_VIEW,
1453 G_PARAM_READWRITE |
1454 G_PARAM_CONSTRUCT_ONLY));
1456 signals[OPEN_COMPONENT] = g_signal_new (
1457 "open-component",
1458 G_TYPE_FROM_CLASS (class),
1459 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1460 G_STRUCT_OFFSET (ETaskTableClass, open_component),
1461 NULL, NULL,
1462 g_cclosure_marshal_VOID__OBJECT,
1463 G_TYPE_NONE, 1,
1464 E_TYPE_CAL_MODEL_COMPONENT);
1466 signals[POPUP_EVENT] = g_signal_new (
1467 "popup-event",
1468 G_TYPE_FROM_CLASS (class),
1469 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1470 G_STRUCT_OFFSET (ETaskTableClass, popup_event),
1471 NULL, NULL,
1472 g_cclosure_marshal_VOID__BOXED,
1473 G_TYPE_NONE, 1,
1474 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1477 static void
1478 e_task_table_init (ETaskTable *task_table)
1480 GtkTargetList *target_list;
1482 task_table->priv = E_TASK_TABLE_GET_PRIVATE (task_table);
1484 task_table->priv->completed_cancellable = NULL;
1486 target_list = gtk_target_list_new (NULL, 0);
1487 e_target_list_add_calendar_targets (target_list, 0);
1488 task_table->priv->copy_target_list = target_list;
1490 target_list = gtk_target_list_new (NULL, 0);
1491 e_target_list_add_calendar_targets (target_list, 0);
1492 task_table->priv->paste_target_list = target_list;
1495 static void
1496 e_task_table_selectable_init (ESelectableInterface *iface)
1498 iface->update_actions = task_table_update_actions;
1499 iface->cut_clipboard = task_table_cut_clipboard;
1500 iface->copy_clipboard = task_table_copy_clipboard;
1501 iface->paste_clipboard = task_table_paste_clipboard;
1502 iface->delete_selection = task_table_delete_selection;
1503 iface->select_all = task_table_select_all;
1507 * e_task_table_new:
1508 * @shell_view: an #EShellView
1509 * @model: an #ECalModel for the table
1511 * Returns a new #ETaskTable.
1513 * Returns: a new #ETaskTable
1515 GtkWidget *
1516 e_task_table_new (EShellView *shell_view,
1517 ECalModel *model)
1519 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1520 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1522 return g_object_new (
1523 E_TYPE_TASK_TABLE,
1524 "model", model, "shell-view", shell_view, NULL);
1528 * e_task_table_get_model:
1529 * @task_table: A calendar table.
1531 * Queries the calendar data model that a calendar table is using.
1533 * Return value: A calendar model.
1535 ECalModel *
1536 e_task_table_get_model (ETaskTable *task_table)
1538 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1540 return task_table->priv->model;
1543 EShellView *
1544 e_task_table_get_shell_view (ETaskTable *task_table)
1546 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1548 return task_table->priv->shell_view;
1551 struct get_selected_uids_closure {
1552 ETaskTable *task_table;
1553 GSList *objects;
1556 /* Used from e_table_selected_row_foreach(), builds a list of the selected UIDs */
1557 static void
1558 add_uid_cb (gint model_row,
1559 gpointer data)
1561 struct get_selected_uids_closure *closure = data;
1562 ECalModelComponent *comp_data;
1563 ECalModel *model;
1565 model = e_task_table_get_model (closure->task_table);
1566 comp_data = e_cal_model_get_component_at (model, model_row);
1568 closure->objects = g_slist_prepend (closure->objects, comp_data);
1572 * e_task_table_get_selected:
1573 * @task_table:
1575 * Get the currently selected ECalModelComponent's on the table.
1577 * Return value: A GSList of the components, which should be
1578 * g_slist_free'd when finished with.
1580 GSList *
1581 e_task_table_get_selected (ETaskTable *task_table)
1583 struct get_selected_uids_closure closure;
1585 closure.task_table = task_table;
1586 closure.objects = NULL;
1588 e_table_selected_row_foreach (
1589 E_TABLE (task_table), add_uid_cb, &closure);
1591 return closure.objects;
1594 GtkTargetList *
1595 e_task_table_get_copy_target_list (ETaskTable *task_table)
1597 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1599 return task_table->priv->copy_target_list;
1602 GtkTargetList *
1603 e_task_table_get_paste_target_list (ETaskTable *task_table)
1605 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1607 return task_table->priv->paste_target_list;
1610 static void
1611 task_table_get_object_list_async (GList *clients_list,
1612 const gchar *sexp,
1613 GCancellable *cancellable,
1614 GAsyncReadyCallback callback,
1615 gpointer callback_data)
1617 GList *l;
1619 for (l = clients_list; l != NULL; l = l->next) {
1620 ECalClient *client = l->data;
1622 e_cal_client_get_object_list (
1623 client, sexp, cancellable,
1624 callback, callback_data);
1628 static void
1629 hide_completed_rows_ready (GObject *source_object,
1630 GAsyncResult *result,
1631 gpointer user_data)
1633 ECalModel *model = user_data;
1634 ECalClient *cal_client;
1635 GSList *m, *objects;
1636 gboolean changed = FALSE;
1637 gint pos;
1638 GPtrArray *comp_objects;
1639 GError *error = NULL;
1641 cal_client = E_CAL_CLIENT (source_object);
1643 e_cal_client_get_object_list_finish (cal_client, result, &objects, &error);
1645 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1646 g_error_free (error);
1647 return;
1649 } else if (error != NULL) {
1650 ESource *source;
1652 source = e_client_get_source (E_CLIENT (source_object));
1654 g_warning (
1655 "%s: Could not get the objects from '%s': %s",
1656 G_STRFUNC,
1657 e_source_get_display_name (source),
1658 error->message);
1660 g_error_free (error);
1661 return;
1664 comp_objects = e_cal_model_get_object_array (model);
1665 g_return_if_fail (comp_objects != NULL);
1667 for (m = objects; m; m = m->next) {
1668 ECalModelComponent *comp_data;
1669 ECalComponentId *id;
1670 ECalComponent *comp = e_cal_component_new ();
1672 e_cal_component_set_icalcomponent (
1673 comp, icalcomponent_new_clone (m->data));
1674 id = e_cal_component_get_id (comp);
1676 comp_data = e_cal_model_get_component_for_client_and_uid (model, cal_client, id);
1677 if (comp_data != NULL) {
1678 e_table_model_pre_change (E_TABLE_MODEL (model));
1679 pos = get_position_in_array (
1680 comp_objects, comp_data);
1681 if (g_ptr_array_remove (comp_objects, comp_data))
1682 g_object_unref (comp_data);
1683 e_table_model_row_deleted (
1684 E_TABLE_MODEL (model), pos);
1685 changed = TRUE;
1687 e_cal_component_free_id (id);
1688 g_object_unref (comp);
1691 e_cal_client_free_icalcomp_slist (objects);
1693 if (changed) {
1694 /* To notify about changes, because in call of
1695 * row_deleted there are still all events. */
1696 e_table_model_changed (E_TABLE_MODEL (model));
1700 static void
1701 show_completed_rows_ready (GObject *source_object,
1702 GAsyncResult *result,
1703 gpointer user_data)
1705 ECalClient *cal_client;
1706 ECalModel *model = user_data;
1707 GSList *m, *objects;
1708 GPtrArray *comp_objects;
1709 GError *error = NULL;
1711 cal_client = E_CAL_CLIENT (source_object);
1712 g_return_if_fail (cal_client != NULL);
1714 e_cal_client_get_object_list_finish (cal_client, result, &objects, &error);
1716 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1717 g_error_free (error);
1718 return;
1720 } else if (error != NULL) {
1721 ESource *source;
1723 source = e_client_get_source (E_CLIENT (source_object));
1725 g_debug (
1726 "%s: Could not get the objects from '%s': %s",
1727 G_STRFUNC,
1728 e_source_get_display_name (source),
1729 error->message);
1731 g_error_free (error);
1732 return;
1735 comp_objects = e_cal_model_get_object_array (model);
1736 g_return_if_fail (comp_objects != NULL);
1738 for (m = objects; m; m = m->next) {
1739 ECalModelComponent *comp_data;
1740 ECalComponentId *id;
1741 ECalComponent *comp = e_cal_component_new ();
1743 e_cal_component_set_icalcomponent (
1744 comp, icalcomponent_new_clone (m->data));
1745 id = e_cal_component_get_id (comp);
1747 if (!(e_cal_model_get_component_for_client_and_uid (model, cal_client, id))) {
1748 e_table_model_pre_change (E_TABLE_MODEL (model));
1749 comp_data = g_object_new (
1750 E_TYPE_CAL_MODEL_COMPONENT, NULL);
1751 comp_data->client = g_object_ref (cal_client);
1752 comp_data->icalcomp =
1753 icalcomponent_new_clone (m->data);
1754 e_cal_model_set_instance_times (
1755 comp_data,
1756 e_cal_model_get_timezone (model));
1757 comp_data->dtstart = NULL;
1758 comp_data->dtend = NULL;
1759 comp_data->due = NULL;
1760 comp_data->completed = NULL;
1761 comp_data->color = NULL;
1763 g_ptr_array_add (comp_objects, comp_data);
1764 e_table_model_row_inserted (
1765 E_TABLE_MODEL (model),
1766 comp_objects->len - 1);
1768 e_cal_component_free_id (id);
1769 g_object_unref (comp);
1772 e_cal_client_free_icalcomp_slist (objects);
1775 /* Returns the current time, for the ECellDateEdit items.
1776 * FIXME: Should probably use the timezone of the item rather than the
1777 * current timezone, though that may be difficult to get from here. */
1778 static struct tm
1779 e_task_table_get_current_time (ECellDateEdit *ecde,
1780 gpointer data)
1782 ETaskTable *task_table = data;
1783 ECalModel *model;
1784 icaltimezone *zone;
1785 struct tm tmp_tm = { 0 };
1786 struct icaltimetype tt;
1788 /* Get the current timezone. */
1789 model = e_task_table_get_model (task_table);
1790 zone = e_cal_model_get_timezone (model);
1792 tt = icaltime_from_timet_with_zone (time (NULL), FALSE, zone);
1794 /* Now copy it to the struct tm and return it. */
1795 tmp_tm.tm_year = tt.year - 1900;
1796 tmp_tm.tm_mon = tt.month - 1;
1797 tmp_tm.tm_mday = tt.day;
1798 tmp_tm.tm_hour = tt.hour;
1799 tmp_tm.tm_min = tt.minute;
1800 tmp_tm.tm_sec = tt.second;
1801 tmp_tm.tm_isdst = -1;
1803 return tmp_tm;
1807 * e_task_table_process_completed_tasks:
1808 * @table: A calendar table model.
1810 * Process completed tasks.
1812 void
1813 e_task_table_process_completed_tasks (ETaskTable *task_table,
1814 gboolean config_changed)
1816 ECalModel *model;
1817 ECalDataModel *data_model;
1818 GList *client_list;
1819 GCancellable *cancellable;
1820 gchar *hide_sexp, *show_sexp;
1822 if (task_table->priv->completed_cancellable) {
1823 g_cancellable_cancel (task_table->priv->completed_cancellable);
1824 g_object_unref (task_table->priv->completed_cancellable);
1827 task_table->priv->completed_cancellable = g_cancellable_new ();
1828 cancellable = task_table->priv->completed_cancellable;
1830 model = e_task_table_get_model (task_table);
1831 data_model = e_cal_model_get_data_model (model);
1832 hide_sexp = calendar_config_get_hide_completed_tasks_sexp (TRUE);
1833 show_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
1835 /* If hide option is unchecked */
1836 if (!(hide_sexp && show_sexp))
1837 show_sexp = g_strdup ("(is-completed?)");
1839 client_list = e_cal_data_model_get_clients (data_model);
1841 /* Delete rows from model */
1842 if (hide_sexp) {
1843 task_table_get_object_list_async (
1844 client_list, hide_sexp, cancellable,
1845 hide_completed_rows_ready, model);
1848 /* Insert rows into model */
1849 if (config_changed) {
1850 task_table_get_object_list_async (
1851 client_list, show_sexp, cancellable,
1852 show_completed_rows_ready, model);
1855 g_list_free_full (client_list, (GDestroyNotify) g_object_unref);
1857 g_free (hide_sexp);
1858 g_free (show_sexp);