No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Damon Chaplin <damon@ximian.com>
18 * Rodrigo Moya <rodrigo@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 /*
25 * ETaskTable - displays the ECalComponent objects in a table (an ETable).
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <glib/gi18n.h>
35 #include <glib/gstdio.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <misc/e-selectable.h>
39 #include <table/e-cell-checkbox.h>
40 #include <table/e-cell-toggle.h>
41 #include <table/e-cell-text.h>
42 #include <table/e-cell-combo.h>
43 #include <table/e-cell-date.h>
44 #include <e-util/e-selection.h>
45 #include <e-util/e-dialog-utils.h>
46 #include <e-util/e-util-private.h>
47 #include <table/e-cell-date-edit.h>
48 #include <table/e-cell-percent.h>
49 #include <table/e-table-sorting-utils.h>
50
51 #include "calendar-config.h"
52 #include "dialogs/delete-comp.h"
53 #include "dialogs/delete-error.h"
54 #include "dialogs/task-editor.h"
55 #include "e-cal-model-tasks.h"
56 #include "e-task-table.h"
57 #include "e-calendar-view.h"
58 #include "e-cell-date-edit-text.h"
59 #include "print.h"
60 #include <e-util/e-util-private.h>
61 #include <e-util/e-icon-factory.h>
62 #include "misc.h"
63
64 #define E_TASK_TABLE_GET_PRIVATE(obj) \
65 (G_TYPE_INSTANCE_GET_PRIVATE \
66 ((obj), E_TYPE_TASK_TABLE, ETaskTablePrivate))
67
68 struct _ETaskTablePrivate {
69 gpointer shell_view; /* weak pointer */
70 ECalModel *model;
71 GCancellable *completed_cancellable; /* when processing completed tasks */
72
73 GtkTargetList *copy_target_list;
74 GtkTargetList *paste_target_list;
75 };
76
77 enum {
78 PROP_0,
79 PROP_COPY_TARGET_LIST,
80 PROP_MODEL,
81 PROP_PASTE_TARGET_LIST,
82 PROP_SHELL_VIEW
83 };
84
85 enum {
86 OPEN_COMPONENT,
87 POPUP_EVENT,
88 STATUS_MESSAGE,
89 LAST_SIGNAL
90 };
91
92 static struct tm e_task_table_get_current_time (ECellDateEdit *ecde, gpointer data);
93
94 static guint signals[LAST_SIGNAL];
95
96 /* The icons to represent the task. */
97 static const gchar *icon_names[] = {
98 "stock_task",
99 "stock_task-recurring",
100 "stock_task-assigned",
101 "stock_task-assigned-to"
102 };
103
104 /* Forward Declarations */
105 static void e_task_table_selectable_init
106 (ESelectableInterface *interface);
107
108 G_DEFINE_TYPE_WITH_CODE (
109 ETaskTable,
110 e_task_table,
111 E_TYPE_TABLE,
112 G_IMPLEMENT_INTERFACE (
113 E_TYPE_SELECTABLE,
114 e_task_table_selectable_init))
115
116 static void
117 task_table_emit_open_component (ETaskTable *task_table,
118 ECalModelComponent *comp_data)
119 {
120 guint signal_id = signals[OPEN_COMPONENT];
121
122 g_signal_emit (task_table, signal_id, 0, comp_data);
123 }
124
125 static void
126 task_table_emit_popup_event (ETaskTable *task_table,
127 GdkEvent *event)
128 {
129 guint signal_id = signals[POPUP_EVENT];
130
131 g_signal_emit (task_table, signal_id, 0, event);
132 }
133
134 static void
135 task_table_emit_status_message (ETaskTable *task_table,
136 const gchar *message,
137 gdouble percent)
138 {
139 guint signal_id = signals[STATUS_MESSAGE];
140
141 g_signal_emit (task_table, signal_id, 0, message, percent);
142 }
143
144 static gint
145 task_table_percent_compare_cb (gconstpointer a,
146 gconstpointer b,
147 gpointer cmp_cache)
148 {
149 gint percent1 = GPOINTER_TO_INT (a);
150 gint percent2 = GPOINTER_TO_INT (b);
151
152 return (percent1 < percent2) ? -1 : (percent1 > percent2);
153 }
154
155 static gint
156 task_table_priority_compare_cb (gconstpointer a,
157 gconstpointer b,
158 gpointer cmp_cache)
159 {
160 gint priority1, priority2;
161
162 priority1 = e_cal_util_priority_from_string ((const gchar *) a);
163 priority2 = e_cal_util_priority_from_string ((const gchar *) b);
164
165 /* We change undefined priorities so they appear after 'Low'. */
166 if (priority1 <= 0)
167 priority1 = 10;
168 if (priority2 <= 0)
169 priority2 = 10;
170
171 /* We'll just use the ordering of the priority values. */
172 return (priority1 < priority2) ? -1 : (priority1 > priority2);
173 }
174
175 static const gchar *
176 get_cache_str (gpointer cmp_cache,
177 const gchar *str)
178 {
179 const gchar *value;
180
181 if (!cmp_cache || !str)
182 return str;
183
184 value = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, str);
185 if (!value) {
186 gchar *ckey;
187
188 ckey = g_utf8_collate_key (str, -1);
189 e_table_sorting_utils_add_to_cmp_cache (cmp_cache, (gchar *) str, ckey);
190 value = ckey;
191 }
192
193 return value;
194 }
195
196 static gboolean
197 same_cache_string (gpointer cmp_cache,
198 const gchar *str_a,
199 const gchar *str_b)
200 {
201 if (!cmp_cache)
202 return g_utf8_collate (str_a, str_b) == 0;
203
204 str_b = get_cache_str (cmp_cache, str_b);
205
206 g_return_val_if_fail (str_a != NULL, FALSE);
207 g_return_val_if_fail (str_b != NULL, FALSE);
208
209 return strcmp (str_a, str_b) == 0;
210 }
211
212 static gint
213 task_table_status_compare_cb (gconstpointer a,
214 gconstpointer b,
215 gpointer cmp_cache)
216 {
217 const gchar *string_a = a;
218 const gchar *string_b = b;
219 gint status_a = -2;
220 gint status_b = -2;
221
222 if (string_a == NULL || *string_a == '\0')
223 status_a = -1;
224 else {
225 const gchar *cache_str = get_cache_str (cmp_cache, string_a);
226
227 if (same_cache_string (cmp_cache, cache_str, _("Not Started")))
228 status_a = 0;
229 else if (same_cache_string (cmp_cache, cache_str, _("In Progress")))
230 status_a = 1;
231 else if (same_cache_string (cmp_cache, cache_str, _("Completed")))
232 status_a = 2;
233 else if (same_cache_string (cmp_cache, cache_str, _("Canceled")))
234 status_a = 3;
235 }
236
237 if (string_b == NULL || *string_b == '\0')
238 status_b = -1;
239 else {
240 const gchar *cache_str = get_cache_str (cmp_cache, string_b);
241
242 if (same_cache_string (cmp_cache, cache_str, _("Not Started")))
243 status_b = 0;
244 else if (same_cache_string (cmp_cache, cache_str, _("In Progress")))
245 status_b = 1;
246 else if (same_cache_string (cmp_cache, cache_str, _("Completed")))
247 status_b = 2;
248 else if (same_cache_string (cmp_cache, cache_str, _("Canceled")))
249 status_b = 3;
250 }
251
252 return (status_a < status_b) ? -1 : (status_a > status_b);
253 }
254
255 static void
256 task_table_model_cal_view_progress_cb (ETaskTable *task_table,
257 const gchar *message,
258 gint progress,
259 ECalClientSourceType type)
260 {
261 gdouble percent = (gdouble) progress;
262
263 task_table_emit_status_message (task_table, message, percent);
264 }
265
266 static void
267 task_table_model_cal_view_complete_cb (ETaskTable *task_table,
268 const GError *error,
269 ECalClientSourceType type)
270 {
271 task_table_emit_status_message (task_table, NULL, -1.0);
272 }
273
274 /* Deletes all of the selected components in the table */
275 static void
276 delete_selected_components (ETaskTable *task_table)
277 {
278 GSList *objs, *l;
279 const gchar *status_message;
280
281 objs = e_task_table_get_selected (task_table);
282
283 status_message = _("Deleting selected objects");
284 task_table_emit_status_message (task_table, status_message, -1.0);
285
286 for (l = objs; l; l = l->next) {
287 ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
288 GError *error = NULL;
289
290 e_cal_client_remove_object_sync (
291 comp_data->client,
292 icalcomponent_get_uid (comp_data->icalcomp),
293 NULL, CALOBJ_MOD_THIS, NULL, &error);
294 delete_error_dialog (error, E_CAL_COMPONENT_TODO);
295 g_clear_error (&error);
296 }
297
298 task_table_emit_status_message (task_table, NULL, -1.0);
299
300 g_slist_free (objs);
301 }
302
303 static void
304 task_table_queue_draw_cb (ECalModelTasks *tasks_model,
305 GParamSpec *param,
306 GtkWidget *task_table)
307 {
308 g_return_if_fail (task_table != NULL);
309
310 gtk_widget_queue_draw (task_table);
311 }
312
313 static void
314 task_table_set_model (ETaskTable *task_table,
315 ECalModel *model)
316 {
317 g_return_if_fail (task_table->priv->model == NULL);
318
319 task_table->priv->model = g_object_ref (model);
320
321 g_signal_connect_swapped (
322 model, "cal-view-progress",
323 G_CALLBACK (task_table_model_cal_view_progress_cb),
324 task_table);
325
326 g_signal_connect_swapped (
327 model, "cal-view-complete",
328 G_CALLBACK (task_table_model_cal_view_complete_cb),
329 task_table);
330
331 /* redraw on drawing options change */
332 g_signal_connect (
333 model, "notify::highlight-due-today",
334 G_CALLBACK (task_table_queue_draw_cb),
335 task_table);
336
337 g_signal_connect (
338 model, "notify::color-due-today",
339 G_CALLBACK (task_table_queue_draw_cb),
340 task_table);
341
342 g_signal_connect (
343 model, "notify::highlight-overdue",
344 G_CALLBACK (task_table_queue_draw_cb),
345 task_table);
346
347 g_signal_connect (
348 model, "notify::color-overdue",
349 G_CALLBACK (task_table_queue_draw_cb),
350 task_table);
351
352 }
353
354 static void
355 task_table_set_shell_view (ETaskTable *task_table,
356 EShellView *shell_view)
357 {
358 g_return_if_fail (task_table->priv->shell_view == NULL);
359
360 task_table->priv->shell_view = shell_view;
361
362 g_object_add_weak_pointer (
363 G_OBJECT (shell_view),
364 &task_table->priv->shell_view);
365 }
366
367 static void
368 task_table_set_property (GObject *object,
369 guint property_id,
370 const GValue *value,
371 GParamSpec *pspec)
372 {
373 switch (property_id) {
374 case PROP_MODEL:
375 task_table_set_model (
376 E_TASK_TABLE (object),
377 g_value_get_object (value));
378 return;
379
380 case PROP_SHELL_VIEW:
381 task_table_set_shell_view (
382 E_TASK_TABLE (object),
383 g_value_get_object (value));
384 return;
385 }
386
387 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
388 }
389
390 static void
391 task_table_get_property (GObject *object,
392 guint property_id,
393 GValue *value,
394 GParamSpec *pspec)
395 {
396 switch (property_id) {
397 case PROP_COPY_TARGET_LIST:
398 g_value_set_boxed (
399 value, e_task_table_get_copy_target_list (
400 E_TASK_TABLE (object)));
401 return;
402
403 case PROP_MODEL:
404 g_value_set_object (
405 value, e_task_table_get_model (
406 E_TASK_TABLE (object)));
407 return;
408
409 case PROP_PASTE_TARGET_LIST:
410 g_value_set_boxed (
411 value, e_task_table_get_paste_target_list (
412 E_TASK_TABLE (object)));
413 return;
414
415 case PROP_SHELL_VIEW:
416 g_value_set_object (
417 value, e_task_table_get_shell_view (
418 E_TASK_TABLE (object)));
419 return;
420 }
421
422 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
423 }
424
425 static void
426 task_table_dispose (GObject *object)
427 {
428 ETaskTablePrivate *priv;
429
430 priv = E_TASK_TABLE_GET_PRIVATE (object);
431
432 if (priv->completed_cancellable) {
433 g_cancellable_cancel (priv->completed_cancellable);
434 g_object_unref (priv->completed_cancellable);
435 priv->completed_cancellable = NULL;
436 }
437
438 if (priv->shell_view != NULL) {
439 g_object_remove_weak_pointer (
440 G_OBJECT (priv->shell_view), &priv->shell_view);
441 priv->shell_view = NULL;
442 }
443
444 if (priv->model != NULL) {
445 g_signal_handlers_disconnect_by_func (priv->model, task_table_queue_draw_cb, object);
446 g_object_unref (priv->model);
447 priv->model = NULL;
448 }
449
450 if (priv->copy_target_list != NULL) {
451 gtk_target_list_unref (priv->copy_target_list);
452 priv->copy_target_list = NULL;
453 }
454
455 if (priv->paste_target_list != NULL) {
456 gtk_target_list_unref (priv->paste_target_list);
457 priv->paste_target_list = NULL;
458 }
459
460 /* Chain up to parent's dispose() method. */
461 G_OBJECT_CLASS (e_task_table_parent_class)->dispose (object);
462 }
463
464 static void
465 task_table_constructed (GObject *object)
466 {
467 ETaskTable *task_table;
468 ECalModel *model;
469 ECell *cell, *popup_cell;
470 ETableExtras *extras;
471 GList *strings;
472 AtkObject *a11y;
473 gchar *etspecfile;
474 gint percent;
475
476 task_table = E_TASK_TABLE (object);
477 model = e_task_table_get_model (task_table);
478
479 /* Create the header columns */
480
481 extras = e_table_extras_new ();
482
483 /*
484 * Normal string fields.
485 */
486 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
487 g_object_set (
488 cell,
489 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
490 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
491 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
492 NULL);
493
494 e_table_extras_add_cell (extras, "calstring", cell);
495 g_object_unref (cell);
496
497 /*
498 * Date fields.
499 */
500 cell = e_cell_date_edit_text_new (NULL, GTK_JUSTIFY_LEFT);
501 g_object_set (
502 cell,
503 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
504 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
505 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
506 NULL);
507
508 g_object_bind_property (
509 model, "timezone",
510 cell, "timezone",
511 G_BINDING_BIDIRECTIONAL |
512 G_BINDING_SYNC_CREATE);
513
514 g_object_bind_property (
515 model, "use-24-hour-format",
516 cell, "use-24-hour-format",
517 G_BINDING_BIDIRECTIONAL |
518 G_BINDING_SYNC_CREATE);
519
520 popup_cell = e_cell_date_edit_new ();
521 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
522 g_object_unref (cell);
523
524 g_object_bind_property (
525 model, "use-24-hour-format",
526 popup_cell, "use-24-hour-format",
527 G_BINDING_BIDIRECTIONAL |
528 G_BINDING_SYNC_CREATE);
529
530 e_table_extras_add_cell (extras, "dateedit", popup_cell);
531 g_object_unref (popup_cell);
532
533 task_table->dates_cell = E_CELL_DATE_EDIT (popup_cell);
534
535 e_cell_date_edit_set_get_time_callback (
536 E_CELL_DATE_EDIT (popup_cell),
537 e_task_table_get_current_time, task_table, NULL);
538
539 /*
540 * Combo fields.
541 */
542
543 /* Classification field. */
544 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
545 g_object_set (
546 cell,
547 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
548 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
549 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
550 "editable", FALSE,
551 NULL);
552
553 popup_cell = e_cell_combo_new ();
554 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
555 g_object_unref (cell);
556
557 strings = NULL;
558 strings = g_list_append (strings, (gchar *) _("Public"));
559 strings = g_list_append (strings, (gchar *) _("Private"));
560 strings = g_list_append (strings, (gchar *) _("Confidential"));
561 e_cell_combo_set_popdown_strings (
562 E_CELL_COMBO (popup_cell),
563 strings);
564 g_list_free (strings);
565
566 e_table_extras_add_cell (extras, "classification", popup_cell);
567 g_object_unref (popup_cell);
568
569 /* Priority field. */
570 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
571 g_object_set (
572 cell,
573 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
574 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
575 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
576 "editable", FALSE,
577 NULL);
578
579 popup_cell = e_cell_combo_new ();
580 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
581 g_object_unref (cell);
582
583 strings = NULL;
584 strings = g_list_append (strings, (gchar *) _("High"));
585 strings = g_list_append (strings, (gchar *) _("Normal"));
586 strings = g_list_append (strings, (gchar *) _("Low"));
587 strings = g_list_append (strings, (gchar *) _("Undefined"));
588 e_cell_combo_set_popdown_strings (
589 E_CELL_COMBO (popup_cell),
590 strings);
591 g_list_free (strings);
592
593 e_table_extras_add_cell (extras, "priority", popup_cell);
594 g_object_unref (popup_cell);
595
596 /* Percent field. */
597 cell = e_cell_percent_new (NULL, GTK_JUSTIFY_LEFT);
598 g_object_set (
599 cell,
600 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
601 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
602 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
603 NULL);
604
605 popup_cell = e_cell_combo_new ();
606 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
607 g_object_unref (cell);
608
609 strings = NULL;
610 for (percent = 0; percent <= 100; percent += 10) {
611 /* Translators: "%d%%" is the percentage of a task done.
612 * %d is the actual value, %% is replaced with a percent sign.
613 * Result values will be 0%, 10%, 20%, ... 100%
614 */
615 strings = g_list_append (strings, g_strdup_printf (_("%d%%"), percent));
616 }
617 e_cell_combo_set_popdown_strings (
618 E_CELL_COMBO (popup_cell),
619 strings);
620
621 g_list_foreach (strings, (GFunc) g_free, NULL);
622 g_list_free (strings);
623
624 e_table_extras_add_cell (extras, "percent", popup_cell);
625 g_object_unref (popup_cell);
626
627 /* Transparency field. */
628 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
629 g_object_set (
630 cell,
631 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
632 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
633 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
634 "editable", FALSE,
635 NULL);
636
637 popup_cell = e_cell_combo_new ();
638 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
639 g_object_unref (cell);
640
641 strings = NULL;
642 strings = g_list_append (strings, (gchar *) _("Free"));
643 strings = g_list_append (strings, (gchar *) _("Busy"));
644 e_cell_combo_set_popdown_strings (
645 E_CELL_COMBO (popup_cell),
646 strings);
647 g_list_free (strings);
648
649 e_table_extras_add_cell (extras, "transparency", popup_cell);
650 g_object_unref (popup_cell);
651
652 /* Status field. */
653 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
654 g_object_set (
655 cell,
656 "strikeout_column", E_CAL_MODEL_TASKS_FIELD_STRIKEOUT,
657 "bold_column", E_CAL_MODEL_TASKS_FIELD_OVERDUE,
658 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
659 "editable", FALSE,
660 NULL);
661
662 popup_cell = e_cell_combo_new ();
663 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
664 g_object_unref (cell);
665
666 strings = NULL;
667 strings = g_list_append (strings, (gchar *) _("Not Started"));
668 strings = g_list_append (strings, (gchar *) _("In Progress"));
669 strings = g_list_append (strings, (gchar *) _("Completed"));
670 strings = g_list_append (strings, (gchar *) _("Canceled"));
671 e_cell_combo_set_popdown_strings (
672 E_CELL_COMBO (popup_cell),
673 strings);
674 g_list_free (strings);
675
676 e_table_extras_add_cell (extras, "calstatus", popup_cell);
677 g_object_unref (popup_cell);
678
679 e_table_extras_add_compare (
680 extras, "date-compare",
681 e_cell_date_edit_compare_cb);
682 e_table_extras_add_compare (
683 extras, "percent-compare",
684 task_table_percent_compare_cb);
685 e_table_extras_add_compare (
686 extras, "priority-compare",
687 task_table_priority_compare_cb);
688 e_table_extras_add_compare (
689 extras, "status-compare",
690 task_table_status_compare_cb);
691
692 /* Create pixmaps */
693
694 cell = e_cell_toggle_new (icon_names, G_N_ELEMENTS (icon_names));
695 e_table_extras_add_cell (extras, "icon", cell);
696 g_object_unref (cell);
697
698 e_table_extras_add_icon_name (extras, "icon", "stock_task");
699
700 e_table_extras_add_icon_name (extras, "complete", "stock_check-filled");
701
702 /* set proper format component for a default 'date' cell renderer */
703 cell = e_table_extras_get_cell (extras, "date");
704 e_cell_date_set_format_component (E_CELL_DATE (cell), "calendar");
705
706 /* Create the table */
707
708 etspecfile = g_build_filename (
709 EVOLUTION_ETSPECDIR, "e-calendar-table.etspec", NULL);
710 e_table_construct_from_spec_file (
711 E_TABLE (task_table), E_TABLE_MODEL (model),
712 extras, etspecfile, NULL);
713 g_free (etspecfile);
714
715 gtk_widget_set_has_tooltip (GTK_WIDGET (task_table), TRUE);
716
717 g_object_unref (extras);
718
719 a11y = gtk_widget_get_accessible (GTK_WIDGET (task_table));
720 if (a11y)
721 atk_object_set_name (a11y, _("Tasks"));
722
723 /* Chain up to parent's constructed() method. */
724 G_OBJECT_CLASS (e_task_table_parent_class)->constructed (object);
725 }
726
727 static gboolean
728 task_table_popup_menu (GtkWidget *widget)
729 {
730 ETaskTable *task_table;
731
732 task_table = E_TASK_TABLE (widget);
733 task_table_emit_popup_event (task_table, NULL);
734
735 return TRUE;
736 }
737
738 static gboolean
739 task_table_query_tooltip (GtkWidget *widget,
740 gint x,
741 gint y,
742 gboolean keyboard_mode,
743 GtkTooltip *tooltip)
744 {
745 ETaskTable *task_table;
746 ECalModel *model;
747 ECalModelComponent *comp_data;
748 gint row = -1, col = -1;
749 GtkWidget *box, *l, *w;
750 GtkStyle *style = gtk_widget_get_default_style ();
751 gchar *tmp;
752 const gchar *str;
753 GString *tmp2;
754 gchar buff[1001];
755 gboolean free_text = FALSE;
756 gboolean use_24_hour_format;
757 ECalComponent *new_comp;
758 ECalComponentOrganizer organizer;
759 ECalComponentDateTime dtstart, dtdue;
760 icalcomponent *clone;
761 icaltimezone *zone, *default_zone;
762 GSList *desc, *p;
763 gint len;
764 ESelectionModel *esm;
765 struct tm tmp_tm;
766
767 if (keyboard_mode)
768 return FALSE;
769
770 task_table = E_TASK_TABLE (widget);
771
772 e_table_get_mouse_over_cell (E_TABLE (task_table), &row, &col);
773 if (row == -1)
774 return FALSE;
775
776 /* Respect sorting option; the 'e_table_get_mouse_over_cell'
777 * returns sorted row, not the model one. */
778 esm = e_table_get_selection_model (E_TABLE (task_table));
779 if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
780 row = e_sorter_sorted_to_model (esm->sorter, row);
781
782 model = e_task_table_get_model (task_table);
783 comp_data = e_cal_model_get_component_at (model, row);
784
785 if (!comp_data || !comp_data->icalcomp)
786 return FALSE;
787
788 new_comp = e_cal_component_new ();
789 clone = icalcomponent_new_clone (comp_data->icalcomp);
790 if (!e_cal_component_set_icalcomponent (new_comp, clone)) {
791 g_object_unref (new_comp);
792 return FALSE;
793 }
794
795 box = gtk_vbox_new (FALSE, 0);
796
797 str = e_calendar_view_get_icalcomponent_summary (
798 comp_data->client, comp_data->icalcomp, &free_text);
799 if (!(str && *str)) {
800 if (free_text)
801 g_free ((gchar *) str);
802 free_text = FALSE;
803 str = _("* No Summary *");
804 }
805
806 l = gtk_label_new (NULL);
807 tmp = g_markup_printf_escaped ("<b>%s</b>", str);
808 gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
809 gtk_label_set_markup (GTK_LABEL (l), tmp);
810 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
811 w = gtk_event_box_new ();
812
813 gtk_widget_modify_bg (w, GTK_STATE_NORMAL, &(style->bg[GTK_STATE_SELECTED]));
814 gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_SELECTED]));
815 gtk_container_add (GTK_CONTAINER (w), l);
816 gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
817 g_free (tmp);
818
819 if (free_text)
820 g_free ((gchar *) str);
821 free_text = FALSE;
822
823 w = gtk_event_box_new ();
824 gtk_widget_modify_bg (w, GTK_STATE_NORMAL, &(style->bg[GTK_STATE_NORMAL]));
825
826 l = gtk_vbox_new (FALSE, 0);
827 gtk_container_add (GTK_CONTAINER (w), l);
828 gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
829 w = l;
830
831 e_cal_component_get_organizer (new_comp, &organizer);
832 if (organizer.cn) {
833 gchar *ptr;
834 ptr = strchr (organizer.value, ':');
835
836 if (ptr) {
837 ptr++;
838 /* To Translators: It will display
839 * "Organizer: NameOfTheUser <email@ofuser.com>" */
840 tmp = g_strdup_printf (_("Organizer: %s <%s>"), organizer.cn, ptr);
841 } else {
842 /* With SunOne accounts, there may be no ':' in
843 * organizer.value. */
844 tmp = g_strdup_printf (_("Organizer: %s"), organizer.cn);
845 }
846
847 l = gtk_label_new (tmp);
848 gtk_label_set_line_wrap (GTK_LABEL (l), FALSE);
849 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
850 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
851 g_free (tmp);
852
853 gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
854 }
855
856 e_cal_component_get_dtstart (new_comp, &dtstart);
857 e_cal_component_get_due (new_comp, &dtdue);
858
859 default_zone = e_cal_model_get_timezone (model);
860 use_24_hour_format = e_cal_model_get_use_24_hour_format (model);
861
862 if (dtstart.tzid) {
863 zone = icalcomponent_get_timezone (
864 e_cal_component_get_icalcomponent (new_comp),
865 dtstart.tzid);
866 if (!zone)
867 e_cal_client_get_timezone_sync (
868 comp_data->client, dtstart.tzid, &zone, NULL, NULL);
869 if (!zone)
870 zone = default_zone;
871 } else {
872 zone = NULL;
873 }
874
875 tmp2 = g_string_new ("");
876
877 if (dtstart.value) {
878 buff[0] = 0;
879
880 tmp_tm = icaltimetype_to_tm_with_zone (
881 dtstart.value, zone, default_zone);
882 e_time_format_date_and_time (
883 &tmp_tm, use_24_hour_format,
884 FALSE, FALSE, buff, 1000);
885
886 if (buff[0]) {
887 g_string_append (tmp2, _("Start: "));
888 g_string_append (tmp2, buff);
889 }
890 }
891
892 if (dtdue.value) {
893 buff[0] = 0;
894
895 tmp_tm = icaltimetype_to_tm_with_zone (
896 dtdue.value, zone, default_zone);
897 e_time_format_date_and_time (
898 &tmp_tm, use_24_hour_format,
899 FALSE, FALSE, buff, 1000);
900
901 if (buff[0]) {
902 if (tmp2->len)
903 g_string_append (tmp2, "; ");
904
905 g_string_append (tmp2, _("Due: "));
906 g_string_append (tmp2, buff);
907 }
908 }
909
910 if (tmp2->len) {
911 l = gtk_label_new (tmp2->str);
912 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
913 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
914
915 gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
916 }
917
918 g_string_free (tmp2, TRUE);
919
920 e_cal_component_free_datetime (&dtstart);
921 e_cal_component_free_datetime (&dtdue);
922
923 tmp = e_cal_model_get_attendees_status_info (
924 model, new_comp, comp_data->client);
925 if (tmp) {
926 l = gtk_label_new (tmp);
927 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
928 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
929
930 g_free (tmp);
931 tmp = NULL;
932
933 gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
934 }
935
936 tmp2 = g_string_new ("");
937 e_cal_component_get_description_list (new_comp, &desc);
938 for (len = 0, p = desc; p != NULL; p = p->next) {
939 ECalComponentText *text = p->data;
940
941 if (text->value != NULL) {
942 len += strlen (text->value);
943 g_string_append (tmp2, text->value);
944 if (len > 1024) {
945 g_string_set_size (tmp2, 1020);
946 g_string_append (tmp2, "...");
947 break;
948 }
949 }
950 }
951 e_cal_component_free_text_list (desc);
952
953 if (tmp2->len) {
954 l = gtk_label_new (tmp2->str);
955 gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
956 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
957 gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
958
959 gtk_widget_modify_fg (l, GTK_STATE_NORMAL, &(style->text[GTK_STATE_NORMAL]));
960 }
961
962 g_string_free (tmp2, TRUE);
963
964 gtk_widget_show_all (box);
965 gtk_tooltip_set_custom (tooltip, box);
966
967 g_object_unref (new_comp);
968
969 return TRUE;
970 }
971
972 static void
973 task_table_double_click (ETable *table,
974 gint row,
975 gint col,
976 GdkEvent *event)
977 {
978 ETaskTable *task_table;
979 ECalModel *model;
980 ECalModelComponent *comp_data;
981
982 task_table = E_TASK_TABLE (table);
983 model = e_task_table_get_model (task_table);
984 comp_data = e_cal_model_get_component_at (model, row);
985 task_table_emit_open_component (task_table, comp_data);
986 }
987
988 static gint
989 task_table_right_click (ETable *table,
990 gint row,
991 gint col,
992 GdkEvent *event)
993 {
994 ETaskTable *task_table;
995
996 task_table = E_TASK_TABLE (table);
997 task_table_emit_popup_event (task_table, event);
998
999 return TRUE;
1000 }
1001
1002 static void
1003 task_table_update_actions (ESelectable *selectable,
1004 EFocusTracker *focus_tracker,
1005 GdkAtom *clipboard_targets,
1006 gint n_clipboard_targets)
1007 {
1008 ETaskTable *task_table;
1009 GtkAction *action;
1010 GtkTargetList *target_list;
1011 GSList *list, *iter;
1012 gboolean can_paste = FALSE;
1013 gboolean sources_are_editable = TRUE;
1014 gboolean sensitive;
1015 const gchar *tooltip;
1016 gint n_selected;
1017 gint ii;
1018
1019 task_table = E_TASK_TABLE (selectable);
1020 n_selected = e_table_selected_count (E_TABLE (task_table));
1021
1022 list = e_task_table_get_selected (task_table);
1023 for (iter = list; iter != NULL && sources_are_editable; iter = iter->next) {
1024 ECalModelComponent *comp_data = iter->data;
1025
1026 sources_are_editable = sources_are_editable &&
1027 !e_client_is_readonly (E_CLIENT (comp_data->client));
1028 }
1029 g_slist_free (list);
1030
1031 target_list = e_selectable_get_paste_target_list (selectable);
1032 for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
1033 can_paste = gtk_target_list_find (
1034 target_list, clipboard_targets[ii], NULL);
1035
1036 action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
1037 sensitive = (n_selected > 0) && sources_are_editable;
1038 tooltip = _("Cut selected tasks to the clipboard");
1039 gtk_action_set_sensitive (action, sensitive);
1040 gtk_action_set_tooltip (action, tooltip);
1041
1042 action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
1043 sensitive = (n_selected > 0);
1044 tooltip = _("Copy selected tasks to the clipboard");
1045 gtk_action_set_sensitive (action, sensitive);
1046 gtk_action_set_tooltip (action, tooltip);
1047
1048 action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
1049 sensitive = sources_are_editable && can_paste;
1050 tooltip = _("Paste tasks from the clipboard");
1051 gtk_action_set_sensitive (action, sensitive);
1052 gtk_action_set_tooltip (action, tooltip);
1053
1054 action = e_focus_tracker_get_delete_selection_action (focus_tracker);
1055 sensitive = (n_selected > 0) && sources_are_editable;
1056 tooltip = _("Delete selected tasks");
1057 gtk_action_set_sensitive (action, sensitive);
1058 gtk_action_set_tooltip (action, tooltip);
1059
1060 action = e_focus_tracker_get_select_all_action (focus_tracker);
1061 sensitive = TRUE;
1062 tooltip = _("Select all visible tasks");
1063 gtk_action_set_sensitive (action, sensitive);
1064 gtk_action_set_tooltip (action, tooltip);
1065 }
1066
1067 static void
1068 task_table_cut_clipboard (ESelectable *selectable)
1069 {
1070 ETaskTable *task_table;
1071
1072 task_table = E_TASK_TABLE (selectable);
1073
1074 e_selectable_copy_clipboard (selectable);
1075 delete_selected_components (task_table);
1076 }
1077
1078 /* Helper for task_table_copy_clipboard() */
1079 static void
1080 copy_row_cb (gint model_row,
1081 gpointer data)
1082 {
1083 ETaskTable *task_table;
1084 ECalModelComponent *comp_data;
1085 ECalModel *model;
1086 gchar *comp_str;
1087 icalcomponent *child;
1088
1089 task_table = E_TASK_TABLE (data);
1090
1091 g_return_if_fail (task_table->tmp_vcal != NULL);
1092
1093 model = e_task_table_get_model (task_table);
1094 comp_data = e_cal_model_get_component_at (model, model_row);
1095 if (!comp_data)
1096 return;
1097
1098 /* Add timezones to the VCALENDAR component. */
1099 e_cal_util_add_timezones_from_component (
1100 task_table->tmp_vcal, comp_data->icalcomp);
1101
1102 /* Add the new component to the VCALENDAR component. */
1103 comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
1104 child = icalparser_parse_string (comp_str);
1105 if (child) {
1106 icalcomponent_add_component (
1107 task_table->tmp_vcal,
1108 icalcomponent_new_clone (child));
1109 icalcomponent_free (child);
1110 }
1111 g_free (comp_str);
1112 }
1113
1114 static void
1115 task_table_copy_clipboard (ESelectable *selectable)
1116 {
1117 ETaskTable *task_table;
1118 GtkClipboard *clipboard;
1119 gchar *comp_str;
1120
1121 task_table = E_TASK_TABLE (selectable);
1122
1123 /* Create a temporary VCALENDAR object. */
1124 task_table->tmp_vcal = e_cal_util_new_top_level ();
1125
1126 e_table_selected_row_foreach (
1127 E_TABLE (task_table), copy_row_cb, task_table);
1128 comp_str = icalcomponent_as_ical_string_r (task_table->tmp_vcal);
1129
1130 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1131 e_clipboard_set_calendar (clipboard, comp_str, -1);
1132 gtk_clipboard_store (clipboard);
1133
1134 g_free (comp_str);
1135
1136 icalcomponent_free (task_table->tmp_vcal);
1137 task_table->tmp_vcal = NULL;
1138 }
1139
1140 /* Helper for calenable_table_paste_clipboard() */
1141 static void
1142 clipboard_get_calendar_data (ETaskTable *task_table,
1143 const gchar *text)
1144 {
1145 icalcomponent *icalcomp;
1146 gchar *uid;
1147 ECalComponent *comp;
1148 ECalModel *model;
1149 ECalClient *client;
1150 icalcomponent_kind kind;
1151 const gchar *status_message;
1152
1153 g_return_if_fail (E_IS_TASK_TABLE (task_table));
1154
1155 if (!text || !*text)
1156 return;
1157
1158 icalcomp = icalparser_parse_string (text);
1159 if (!icalcomp)
1160 return;
1161
1162 /* check the type of the component */
1163 kind = icalcomponent_isa (icalcomp);
1164 if (kind != ICAL_VCALENDAR_COMPONENT &&
1165 kind != ICAL_VEVENT_COMPONENT &&
1166 kind != ICAL_VTODO_COMPONENT &&
1167 kind != ICAL_VJOURNAL_COMPONENT) {
1168 return;
1169 }
1170
1171 model = e_task_table_get_model (task_table);
1172 client = e_cal_model_get_default_client (model);
1173
1174 status_message = _("Updating objects");
1175 task_table_emit_status_message (task_table, status_message, -1.0);
1176
1177 if (kind == ICAL_VCALENDAR_COMPONENT) {
1178 icalcomponent_kind child_kind;
1179 icalcomponent *subcomp;
1180 icalcomponent *vcal_comp;
1181
1182 vcal_comp = icalcomp;
1183 subcomp = icalcomponent_get_first_component (
1184 vcal_comp, ICAL_ANY_COMPONENT);
1185 while (subcomp) {
1186 child_kind = icalcomponent_isa (subcomp);
1187 if (child_kind == ICAL_VEVENT_COMPONENT ||
1188 child_kind == ICAL_VTODO_COMPONENT ||
1189 child_kind == ICAL_VJOURNAL_COMPONENT) {
1190 ECalComponent *tmp_comp;
1191 GError *error = NULL;
1192
1193 uid = e_cal_component_gen_uid ();
1194 tmp_comp = e_cal_component_new ();
1195 e_cal_component_set_icalcomponent (
1196 tmp_comp,
1197 icalcomponent_new_clone (subcomp));
1198 e_cal_component_set_uid (tmp_comp, uid);
1199 g_free (uid);
1200 uid = NULL;
1201
1202 /* FIXME should we convert start/due/complete
1203 * times? Also, need error handling. */
1204 if (!e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (tmp_comp), &uid, NULL, &error))
1205 uid = NULL;
1206
1207 if (error != NULL) {
1208 g_warning (
1209 "%s: Failed to create object: %s",
1210 G_STRFUNC, error->message);
1211 g_error_free (error);
1212 }
1213
1214 g_object_unref (tmp_comp);
1215 g_free (uid);
1216 }
1217 subcomp = icalcomponent_get_next_component (
1218 vcal_comp, ICAL_ANY_COMPONENT);
1219 }
1220 } else {
1221 GError *error = NULL;
1222
1223 comp = e_cal_component_new ();
1224 e_cal_component_set_icalcomponent (comp, icalcomp);
1225 uid = e_cal_component_gen_uid ();
1226 e_cal_component_set_uid (comp, (const gchar *) uid);
1227 g_free (uid);
1228 uid = NULL;
1229
1230 if (!e_cal_client_create_object_sync (client, e_cal_component_get_icalcomponent (comp), &uid, NULL, &error))
1231 uid = NULL;
1232
1233 if (error != NULL) {
1234 g_warning (
1235 "%s: Failed to create object: %s",
1236 G_STRFUNC, error->message);
1237 g_error_free (error);
1238 }
1239
1240 g_object_unref (comp);
1241 g_free (uid);
1242 }
1243
1244 task_table_emit_status_message (task_table, NULL, -1.0);
1245 }
1246
1247 static void
1248 task_table_paste_clipboard (ESelectable *selectable)
1249 {
1250 ETaskTable *task_table;
1251 GtkClipboard *clipboard;
1252 GnomeCanvasItem *item;
1253 GnomeCanvas *table_canvas;
1254
1255 task_table = E_TASK_TABLE (selectable);
1256
1257 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1258
1259 table_canvas = E_TABLE (task_table)->table_canvas;
1260 item = table_canvas->focused_item;
1261
1262 /* XXX Should ECellText implement GtkEditable? */
1263
1264 /* Paste text into a cell being edited. */
1265 if (gtk_clipboard_wait_is_text_available (clipboard) &&
1266 gtk_widget_has_focus (GTK_WIDGET (table_canvas)) &&
1267 E_IS_TABLE_ITEM (item) &&
1268 E_TABLE_ITEM (item)->editing_col >= 0 &&
1269 E_TABLE_ITEM (item)->editing_row >= 0) {
1270
1271 ETableItem *etable_item = E_TABLE_ITEM (item);
1272
1273 e_cell_text_paste_clipboard (
1274 etable_item->cell_views[etable_item->editing_col],
1275 etable_item->editing_col,
1276 etable_item->editing_row);
1277
1278 /* Paste iCalendar data into the table. */
1279 } else if (e_clipboard_wait_is_calendar_available (clipboard)) {
1280 gchar *calendar_source;
1281
1282 calendar_source = e_clipboard_wait_for_calendar (clipboard);
1283 clipboard_get_calendar_data (task_table, calendar_source);
1284 g_free (calendar_source);
1285 }
1286 }
1287
1288 /* Used from e_table_selected_row_foreach(); puts the selected row number in an
1289 * gint pointed to by the closure data.
1290 */
1291 static void
1292 get_selected_row_cb (gint model_row,
1293 gpointer data)
1294 {
1295 gint *row;
1296
1297 row = data;
1298 *row = model_row;
1299 }
1300
1301 /*
1302 * Returns the component that is selected in the table; only works if there is
1303 * one and only one selected row.
1304 */
1305 static ECalModelComponent *
1306 get_selected_comp (ETaskTable *task_table)
1307 {
1308 ECalModel *model;
1309 gint row;
1310
1311 model = e_task_table_get_model (task_table);
1312 if (e_table_selected_count (E_TABLE (task_table)) != 1)
1313 return NULL;
1314
1315 row = -1;
1316 e_table_selected_row_foreach (
1317 E_TABLE (task_table), get_selected_row_cb, &row);
1318 g_return_val_if_fail (row != -1, NULL);
1319
1320 return e_cal_model_get_component_at (model, row);
1321 }
1322
1323 static void
1324 add_retract_data (ECalComponent *comp,
1325 const gchar *retract_comment)
1326 {
1327 icalcomponent *icalcomp = NULL;
1328 icalproperty *icalprop = NULL;
1329
1330 icalcomp = e_cal_component_get_icalcomponent (comp);
1331 if (retract_comment && *retract_comment)
1332 icalprop = icalproperty_new_x (retract_comment);
1333 else
1334 icalprop = icalproperty_new_x ("0");
1335 icalproperty_set_x_name (icalprop, "X-EVOLUTION-RETRACT-COMMENT");
1336 icalcomponent_add_property (icalcomp, icalprop);
1337 }
1338
1339 static gboolean
1340 check_for_retract (ECalComponent *comp,
1341 ECalClient *client)
1342 {
1343 ECalComponentOrganizer org;
1344 gchar *email = NULL;
1345 const gchar *strip = NULL;
1346 gboolean ret_val;
1347
1348 if (!e_cal_component_has_attendees (comp))
1349 return FALSE;
1350
1351 if (!e_cal_client_check_save_schedules (client))
1352 return FALSE;
1353
1354 e_cal_component_get_organizer (comp, &org);
1355 strip = itip_strip_mailto (org.value);
1356
1357 ret_val = e_client_get_backend_property_sync (
1358 E_CLIENT (client),
1359 CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
1360 &email, NULL, NULL) && email != NULL &&
1361 g_ascii_strcasecmp (email, strip) == 0;
1362
1363 g_free (email);
1364
1365 return ret_val;
1366 }
1367
1368 static void
1369 task_table_delete_selection (ESelectable *selectable)
1370 {
1371 ECalModel *model;
1372 ETaskTable *task_table;
1373 ECalModelComponent *comp_data;
1374 ECalComponent *comp = NULL;
1375 gboolean delete = TRUE;
1376 gint n_selected;
1377 GError *error = NULL;
1378
1379 task_table = E_TASK_TABLE (selectable);
1380 model = e_task_table_get_model (task_table);
1381
1382 n_selected = e_table_selected_count (E_TABLE (task_table));
1383 if (n_selected <= 0)
1384 return;
1385
1386 if (n_selected == 1)
1387 comp_data = get_selected_comp (task_table);
1388 else
1389 comp_data = NULL;
1390
1391 /* FIXME: this may be something other than a TODO component */
1392
1393 if (comp_data) {
1394 comp = e_cal_component_new ();
1395 e_cal_component_set_icalcomponent (
1396 comp, icalcomponent_new_clone (comp_data->icalcomp));
1397 }
1398
1399 if ((n_selected == 1) && comp && check_for_retract (comp, comp_data->client)) {
1400 gchar *retract_comment = NULL;
1401 gboolean retract = FALSE;
1402
1403 delete = prompt_retract_dialog (
1404 comp, &retract_comment,
1405 GTK_WIDGET (task_table), &retract);
1406 if (retract) {
1407 GSList *users = NULL;
1408 icalcomponent *icalcomp = NULL, *mod_comp = NULL;
1409
1410 add_retract_data (comp, retract_comment);
1411 icalcomp = e_cal_component_get_icalcomponent (comp);
1412 icalcomponent_set_method (icalcomp, ICAL_METHOD_CANCEL);
1413 if (!e_cal_client_send_objects_sync (comp_data->client, icalcomp, &users, &mod_comp, NULL, &error)) {
1414 delete_error_dialog (error, E_CAL_COMPONENT_TODO);
1415 g_clear_error (&error);
1416 error = NULL;
1417 } else {
1418
1419 if (mod_comp)
1420 icalcomponent_free (mod_comp);
1421
1422 if (users) {
1423 g_slist_foreach (users, (GFunc) g_free, NULL);
1424 g_slist_free (users);
1425 }
1426 }
1427
1428 }
1429 } else if (e_cal_model_get_confirm_delete (model))
1430 delete = delete_component_dialog (
1431 comp, FALSE, n_selected,
1432 E_CAL_COMPONENT_TODO,
1433 GTK_WIDGET (task_table));
1434
1435 if (delete)
1436 delete_selected_components (task_table);
1437
1438 /* free memory */
1439 if (comp)
1440 g_object_unref (comp);
1441 }
1442
1443 static void
1444 task_table_select_all (ESelectable *selectable)
1445 {
1446 e_table_select_all (E_TABLE (selectable));
1447 }
1448
1449 static void
1450 e_task_table_class_init (ETaskTableClass *class)
1451 {
1452 GObjectClass *object_class;
1453 GtkWidgetClass *widget_class;
1454 ETableClass *table_class;
1455
1456 g_type_class_add_private (class, sizeof (ETaskTablePrivate));
1457
1458 object_class = G_OBJECT_CLASS (class);
1459 object_class->set_property = task_table_set_property;
1460 object_class->get_property = task_table_get_property;
1461 object_class->dispose = task_table_dispose;
1462 object_class->constructed = task_table_constructed;
1463
1464 widget_class = GTK_WIDGET_CLASS (class);
1465 widget_class->popup_menu = task_table_popup_menu;
1466 widget_class->query_tooltip = task_table_query_tooltip;
1467
1468 table_class = E_TABLE_CLASS (class);
1469 table_class->double_click = task_table_double_click;
1470 table_class->right_click = task_table_right_click;
1471
1472 /* Inherited from ESelectableInterface */
1473 g_object_class_override_property (
1474 object_class,
1475 PROP_COPY_TARGET_LIST,
1476 "copy-target-list");
1477
1478 g_object_class_install_property (
1479 object_class,
1480 PROP_MODEL,
1481 g_param_spec_object (
1482 "model",
1483 "Model",
1484 NULL,
1485 E_TYPE_CAL_MODEL,
1486 G_PARAM_READWRITE |
1487 G_PARAM_CONSTRUCT_ONLY));
1488
1489 /* Inherited from ESelectableInterface */
1490 g_object_class_override_property (
1491 object_class,
1492 PROP_PASTE_TARGET_LIST,
1493 "paste-target-list");
1494
1495 g_object_class_install_property (
1496 object_class,
1497 PROP_SHELL_VIEW,
1498 g_param_spec_object (
1499 "shell-view",
1500 "Shell View",
1501 NULL,
1502 E_TYPE_SHELL_VIEW,
1503 G_PARAM_READWRITE |
1504 G_PARAM_CONSTRUCT_ONLY));
1505
1506 signals[OPEN_COMPONENT] = g_signal_new (
1507 "open-component",
1508 G_TYPE_FROM_CLASS (class),
1509 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1510 G_STRUCT_OFFSET (ETaskTableClass, open_component),
1511 NULL, NULL,
1512 g_cclosure_marshal_VOID__OBJECT,
1513 G_TYPE_NONE, 1,
1514 E_TYPE_CAL_MODEL_COMPONENT);
1515
1516 signals[POPUP_EVENT] = g_signal_new (
1517 "popup-event",
1518 G_TYPE_FROM_CLASS (class),
1519 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1520 G_STRUCT_OFFSET (ETaskTableClass, popup_event),
1521 NULL, NULL,
1522 g_cclosure_marshal_VOID__BOXED,
1523 G_TYPE_NONE, 1,
1524 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1525
1526 signals[STATUS_MESSAGE] = g_signal_new (
1527 "status-message",
1528 G_TYPE_FROM_CLASS (class),
1529 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1530 G_STRUCT_OFFSET (ETaskTableClass, status_message),
1531 NULL, NULL,
1532 e_marshal_VOID__STRING_DOUBLE,
1533 G_TYPE_NONE, 2,
1534 G_TYPE_STRING, G_TYPE_DOUBLE);
1535 }
1536
1537 static void
1538 e_task_table_init (ETaskTable *task_table)
1539 {
1540 GtkTargetList *target_list;
1541
1542 task_table->priv = E_TASK_TABLE_GET_PRIVATE (task_table);
1543
1544 task_table->priv->completed_cancellable = NULL;
1545
1546 target_list = gtk_target_list_new (NULL, 0);
1547 e_target_list_add_calendar_targets (target_list, 0);
1548 task_table->priv->copy_target_list = target_list;
1549
1550 target_list = gtk_target_list_new (NULL, 0);
1551 e_target_list_add_calendar_targets (target_list, 0);
1552 task_table->priv->paste_target_list = target_list;
1553 }
1554
1555 static void
1556 e_task_table_selectable_init (ESelectableInterface *interface)
1557 {
1558 interface->update_actions = task_table_update_actions;
1559 interface->cut_clipboard = task_table_cut_clipboard;
1560 interface->copy_clipboard = task_table_copy_clipboard;
1561 interface->paste_clipboard = task_table_paste_clipboard;
1562 interface->delete_selection = task_table_delete_selection;
1563 interface->select_all = task_table_select_all;
1564 }
1565
1566 /**
1567 * e_task_table_new:
1568 * @shell_view: an #EShellView
1569 * @model: an #ECalModel for the table
1570 *
1571 * Returns a new #ETaskTable.
1572 *
1573 * Returns: a new #ETaskTable
1574 **/
1575 GtkWidget *
1576 e_task_table_new (EShellView *shell_view,
1577 ECalModel *model)
1578 {
1579 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1580 g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1581
1582 return g_object_new (
1583 E_TYPE_TASK_TABLE,
1584 "model", model, "shell-view", shell_view, NULL);
1585 }
1586
1587 /**
1588 * e_task_table_get_model:
1589 * @task_table: A calendar table.
1590 *
1591 * Queries the calendar data model that a calendar table is using.
1592 *
1593 * Return value: A calendar model.
1594 **/
1595 ECalModel *
1596 e_task_table_get_model (ETaskTable *task_table)
1597 {
1598 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1599
1600 return task_table->priv->model;
1601 }
1602
1603 EShellView *
1604 e_task_table_get_shell_view (ETaskTable *task_table)
1605 {
1606 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1607
1608 return task_table->priv->shell_view;
1609 }
1610
1611 struct get_selected_uids_closure {
1612 ETaskTable *task_table;
1613 GSList *objects;
1614 };
1615
1616 /* Used from e_table_selected_row_foreach(), builds a list of the selected UIDs */
1617 static void
1618 add_uid_cb (gint model_row,
1619 gpointer data)
1620 {
1621 struct get_selected_uids_closure *closure = data;
1622 ECalModelComponent *comp_data;
1623 ECalModel *model;
1624
1625 model = e_task_table_get_model (closure->task_table);
1626 comp_data = e_cal_model_get_component_at (model, model_row);
1627
1628 closure->objects = g_slist_prepend (closure->objects, comp_data);
1629 }
1630
1631 /**
1632 * e_task_table_get_selected:
1633 * @task_table:
1634 *
1635 * Get the currently selected ECalModelComponent's on the table.
1636 *
1637 * Return value: A GSList of the components, which should be
1638 * g_slist_free'd when finished with.
1639 **/
1640 GSList *
1641 e_task_table_get_selected (ETaskTable *task_table)
1642 {
1643 struct get_selected_uids_closure closure;
1644
1645 closure.task_table = task_table;
1646 closure.objects = NULL;
1647
1648 e_table_selected_row_foreach (
1649 E_TABLE (task_table), add_uid_cb, &closure);
1650
1651 return closure.objects;
1652 }
1653
1654 GtkTargetList *
1655 e_task_table_get_copy_target_list (ETaskTable *task_table)
1656 {
1657 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1658
1659 return task_table->priv->copy_target_list;
1660 }
1661
1662 GtkTargetList *
1663 e_task_table_get_paste_target_list (ETaskTable *task_table)
1664 {
1665 g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL);
1666
1667 return task_table->priv->paste_target_list;
1668 }
1669
1670 static void
1671 task_table_get_object_list_async (GList *clients_list,
1672 const gchar *sexp,
1673 GCancellable *cancellable,
1674 GAsyncReadyCallback callback,
1675 gpointer callback_data)
1676 {
1677 GList *l;
1678
1679 for (l = clients_list; l != NULL; l = l->next) {
1680 ECalClient *client = l->data;
1681
1682 e_cal_client_get_object_list (
1683 client, sexp, cancellable,
1684 callback, callback_data);
1685 }
1686 }
1687
1688 static void
1689 hide_completed_rows_ready (GObject *source_object,
1690 GAsyncResult *result,
1691 gpointer user_data)
1692 {
1693 ECalModel *model = user_data;
1694 GSList *m, *objects;
1695 gboolean changed = FALSE;
1696 gint pos;
1697 GPtrArray *comp_objects;
1698 GError *error = NULL;
1699
1700 e_cal_client_get_object_list_finish (
1701 E_CAL_CLIENT (source_object), result, &objects, &error);
1702
1703 if (error != NULL) {
1704 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1705 !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
1706 ESource *source = e_client_get_source (E_CLIENT (source_object));
1707
1708 g_debug (
1709 "%s: Could not get the objects from '%s': %s",
1710 G_STRFUNC,
1711 e_source_get_display_name (source),
1712 error->message);
1713 }
1714 g_error_free (error);
1715 return;
1716 }
1717
1718 comp_objects = e_cal_model_get_object_array (model);
1719 g_return_if_fail (comp_objects != NULL);
1720
1721 for (m = objects; m; m = m->next) {
1722 ECalModelComponent *comp_data;
1723 ECalComponentId *id;
1724 ECalComponent *comp = e_cal_component_new ();
1725
1726 e_cal_component_set_icalcomponent (
1727 comp, icalcomponent_new_clone (m->data));
1728 id = e_cal_component_get_id (comp);
1729
1730 comp_data = e_cal_model_get_component_for_uid (model, id);
1731 if (comp_data != NULL) {
1732 e_table_model_pre_change (E_TABLE_MODEL (model));
1733 pos = get_position_in_array (
1734 comp_objects, comp_data);
1735 e_table_model_row_deleted (
1736 E_TABLE_MODEL (model), pos);
1737 changed = TRUE;
1738
1739 if (g_ptr_array_remove (comp_objects, comp_data))
1740 g_object_unref (comp_data);
1741 }
1742 e_cal_component_free_id (id);
1743 g_object_unref (comp);
1744 }
1745
1746 e_cal_client_free_icalcomp_slist (objects);
1747
1748 if (changed) {
1749 /* To notify about changes, because in call of
1750 * row_deleted there are still all events. */
1751 e_table_model_changed (E_TABLE_MODEL (model));
1752 }
1753 }
1754
1755 static void
1756 show_completed_rows_ready (GObject *source_object,
1757 GAsyncResult *result,
1758 gpointer user_data)
1759 {
1760 ECalClient *client;
1761 ECalModel *model = user_data;
1762 GSList *m, *objects;
1763 GPtrArray *comp_objects;
1764 GError *error = NULL;
1765
1766 e_cal_client_get_object_list_finish (
1767 E_CAL_CLIENT (source_object), result, &objects, &error);
1768
1769 if (error != NULL) {
1770 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1771 !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
1772 ESource *source = e_client_get_source (E_CLIENT (source_object));
1773
1774 g_debug (
1775 "%s: Could not get the objects from '%s': %s",
1776 G_STRFUNC,
1777 e_source_get_display_name (source),
1778 error->message);
1779 }
1780 g_error_free (error);
1781 return;
1782 }
1783
1784 client = E_CAL_CLIENT (source_object);
1785 g_return_if_fail (client != NULL);
1786
1787 comp_objects = e_cal_model_get_object_array (model);
1788 g_return_if_fail (comp_objects != NULL);
1789
1790 for (m = objects; m; m = m->next) {
1791 ECalModelComponent *comp_data;
1792 ECalComponentId *id;
1793 ECalComponent *comp = e_cal_component_new ();
1794
1795 e_cal_component_set_icalcomponent (
1796 comp, icalcomponent_new_clone (m->data));
1797 id = e_cal_component_get_id (comp);
1798
1799 if (!(e_cal_model_get_component_for_uid (model, id))) {
1800 e_table_model_pre_change (E_TABLE_MODEL (model));
1801 comp_data = g_object_new (
1802 E_TYPE_CAL_MODEL_COMPONENT, NULL);
1803 comp_data->client = g_object_ref (client);
1804 comp_data->icalcomp =
1805 icalcomponent_new_clone (m->data);
1806 e_cal_model_set_instance_times (
1807 comp_data,
1808 e_cal_model_get_timezone (model));
1809 comp_data->dtstart = NULL;
1810 comp_data->dtend = NULL;
1811 comp_data->due = NULL;
1812 comp_data->completed = NULL;
1813 comp_data->color = NULL;
1814
1815 g_ptr_array_add (comp_objects, comp_data);
1816 e_table_model_row_inserted (
1817 E_TABLE_MODEL (model),
1818 comp_objects->len - 1);
1819 }
1820 e_cal_component_free_id (id);
1821 g_object_unref (comp);
1822 }
1823
1824 e_cal_client_free_icalcomp_slist (objects);
1825 }
1826
1827 /* Returns the current time, for the ECellDateEdit items.
1828 * FIXME: Should probably use the timezone of the item rather than the
1829 * current timezone, though that may be difficult to get from here. */
1830 static struct tm
1831 e_task_table_get_current_time (ECellDateEdit *ecde,
1832 gpointer data)
1833 {
1834 ETaskTable *task_table = data;
1835 ECalModel *model;
1836 icaltimezone *zone;
1837 struct tm tmp_tm = { 0 };
1838 struct icaltimetype tt;
1839
1840 /* Get the current timezone. */
1841 model = e_task_table_get_model (task_table);
1842 zone = e_cal_model_get_timezone (model);
1843
1844 tt = icaltime_from_timet_with_zone (time (NULL), FALSE, zone);
1845
1846 /* Now copy it to the struct tm and return it. */
1847 tmp_tm.tm_year = tt.year - 1900;
1848 tmp_tm.tm_mon = tt.month - 1;
1849 tmp_tm.tm_mday = tt.day;
1850 tmp_tm.tm_hour = tt.hour;
1851 tmp_tm.tm_min = tt.minute;
1852 tmp_tm.tm_sec = tt.second;
1853 tmp_tm.tm_isdst = -1;
1854
1855 return tmp_tm;
1856 }
1857
1858 /**
1859 * e_task_table_hide_completed_tasks:
1860 * @table: A calendar table model.
1861 * @client_list: Clients List
1862 *
1863 * Hide completed tasks.
1864 */
1865 void
1866 e_task_table_process_completed_tasks (ETaskTable *task_table,
1867 GList *clients_list,
1868 gboolean config_changed)
1869 {
1870 ECalModel *model;
1871 GCancellable *cancellable;
1872 gchar *hide_sexp, *show_sexp;
1873
1874 if (task_table->priv->completed_cancellable) {
1875 g_cancellable_cancel (task_table->priv->completed_cancellable);
1876 g_object_unref (task_table->priv->completed_cancellable);
1877 }
1878
1879 task_table->priv->completed_cancellable = g_cancellable_new ();
1880 cancellable = task_table->priv->completed_cancellable;
1881
1882 model = e_task_table_get_model (task_table);
1883 hide_sexp = calendar_config_get_hide_completed_tasks_sexp (TRUE);
1884 show_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
1885
1886 /* If hide option is unchecked */
1887 if (!(hide_sexp && show_sexp))
1888 show_sexp = g_strdup ("(is-completed?)");
1889
1890 /* Delete rows from model*/
1891 if (hide_sexp) {
1892 task_table_get_object_list_async (
1893 clients_list, hide_sexp, cancellable,
1894 hide_completed_rows_ready, model);
1895 }
1896
1897 /* Insert rows into model */
1898 if (config_changed) {
1899 task_table_get_object_list_async (
1900 clients_list, show_sexp, cancellable,
1901 show_completed_rows_ready, model);
1902 }
1903
1904 g_free (hide_sexp);
1905 g_free (show_sexp);
1906 }