No issues found
1 /*
2 * e-task-shell-view-private.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "e-task-shell-view-private.h"
27
28 #include "widgets/menus/gal-view-factory-etable.h"
29
30 #include "e-util/e-util-private.h"
31
32 static void
33 task_shell_view_model_row_appended_cb (ETaskShellView *task_shell_view,
34 ECalModel *model)
35 {
36 ETaskShellSidebar *task_shell_sidebar;
37 ECalClient *client;
38 ESource *source;
39
40 /* This is the "Click to Add" handler. */
41
42 client = e_cal_model_get_default_client (model);
43 source = e_client_get_source (E_CLIENT (client));
44
45 task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
46 e_task_shell_sidebar_add_source (task_shell_sidebar, source);
47 }
48
49 static gboolean
50 task_shell_view_process_completed_tasks (ETaskShellView *task_shell_view)
51 {
52 ETaskShellContent *task_shell_content;
53 ETaskShellSidebar *task_shell_sidebar;
54 ETaskTable *task_table;
55 GList *clients;
56
57 task_shell_view->priv->update_completed_timeout = 0;
58
59 task_shell_content = task_shell_view->priv->task_shell_content;
60 task_table = e_task_shell_content_get_task_table (task_shell_content);
61
62 task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
63 clients = e_task_shell_sidebar_get_clients (task_shell_sidebar);
64
65 e_task_table_process_completed_tasks (task_table, clients, TRUE);
66
67 /* Search query takes whether to show completed tasks into account,
68 * so if the preference has changed we need to update the query. */
69 e_shell_view_execute_search (E_SHELL_VIEW (task_shell_view));
70
71 g_list_free (clients);
72
73 return FALSE;
74 }
75
76 static void
77 task_shell_view_schedule_process_completed_tasks (ETaskShellView *task_shell_view)
78 {
79 guint source_id;
80
81 source_id = task_shell_view->priv->update_completed_timeout;
82
83 if (source_id > 0)
84 g_source_remove (source_id);
85
86 source_id = g_timeout_add_seconds (
87 1, (GSourceFunc) task_shell_view_process_completed_tasks,
88 task_shell_view);
89
90 task_shell_view->priv->update_completed_timeout = source_id;
91 }
92
93 static void
94 task_shell_view_table_popup_event_cb (EShellView *shell_view,
95 GdkEventButton *event)
96 {
97 const gchar *widget_path;
98
99 widget_path = "/task-popup";
100 e_shell_view_show_popup_menu (shell_view, widget_path, event);
101 }
102
103 static void
104 task_shell_view_selector_client_added_cb (ETaskShellView *task_shell_view,
105 ECalClient *client)
106 {
107 ETaskShellContent *task_shell_content;
108 ETaskTable *task_table;
109 ECalModel *model;
110
111 task_shell_content = task_shell_view->priv->task_shell_content;
112 task_table = e_task_shell_content_get_task_table (task_shell_content);
113 model = e_task_table_get_model (task_table);
114
115 e_cal_model_add_client (model, client);
116 e_task_shell_view_update_timezone (task_shell_view);
117 }
118
119 static void
120 task_shell_view_selector_client_removed_cb (ETaskShellView *task_shell_view,
121 ECalClient *client)
122 {
123 ETaskShellContent *task_shell_content;
124 ETaskTable *task_table;
125 ECalModel *model;
126
127 task_shell_content = task_shell_view->priv->task_shell_content;
128 task_table = e_task_shell_content_get_task_table (task_shell_content);
129 model = e_task_table_get_model (task_table);
130
131 e_cal_model_remove_client (model, client);
132 }
133
134 static gboolean
135 task_shell_view_selector_popup_event_cb (EShellView *shell_view,
136 ESource *primary_source,
137 GdkEventButton *event)
138 {
139 const gchar *widget_path;
140
141 widget_path = "/task-list-popup";
142 e_shell_view_show_popup_menu (shell_view, widget_path, event);
143
144 return TRUE;
145 }
146
147 static gboolean
148 task_shell_view_update_timeout_cb (ETaskShellView *task_shell_view)
149 {
150 ETaskShellContent *task_shell_content;
151 ETaskShellSidebar *task_shell_sidebar;
152 ETaskTable *task_table;
153 ECalModel *model;
154 GList *clients;
155
156 task_shell_content = task_shell_view->priv->task_shell_content;
157 task_table = e_task_shell_content_get_task_table (task_shell_content);
158 model = e_task_table_get_model (task_table);
159
160 task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
161 clients = e_task_shell_sidebar_get_clients (task_shell_sidebar);
162
163 e_task_table_process_completed_tasks (task_table, clients, FALSE);
164 e_cal_model_tasks_update_due_tasks (E_CAL_MODEL_TASKS (model));
165
166 g_list_free (clients);
167
168 return TRUE;
169 }
170
171 static void
172 task_shell_view_load_view_collection (EShellViewClass *shell_view_class)
173 {
174 GalViewCollection *collection;
175 GalViewFactory *factory;
176 ETableSpecification *spec;
177 const gchar *base_dir;
178 gchar *filename;
179
180 collection = shell_view_class->view_collection;
181
182 base_dir = EVOLUTION_ETSPECDIR;
183 spec = e_table_specification_new ();
184 filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
185 if (!e_table_specification_load_from_file (spec, filename))
186 g_critical (
187 "Unable to load ETable specification file "
188 "for tasks");
189 g_free (filename);
190
191 factory = gal_view_factory_etable_new (spec);
192 gal_view_collection_add_factory (collection, factory);
193 g_object_unref (factory);
194 g_object_unref (spec);
195
196 gal_view_collection_load (collection);
197 }
198
199 static void
200 task_shell_view_notify_view_id_cb (ETaskShellView *task_shell_view)
201 {
202 ETaskShellContent *task_shell_content;
203 GalViewInstance *view_instance;
204 const gchar *view_id;
205
206 task_shell_content = task_shell_view->priv->task_shell_content;
207 view_instance =
208 e_task_shell_content_get_view_instance (task_shell_content);
209 view_id = e_shell_view_get_view_id (E_SHELL_VIEW (task_shell_view));
210
211 /* A NULL view ID implies we're in a custom view. But you can
212 * only get to a custom view via the "Define Views" dialog, which
213 * would have already modified the view instance appropriately.
214 * Furthermore, there's no way to refer to a custom view by ID
215 * anyway, since custom views have no IDs. */
216 if (view_id == NULL)
217 return;
218
219 gal_view_instance_set_current_view_id (view_instance, view_id);
220 }
221
222 void
223 e_task_shell_view_private_init (ETaskShellView *task_shell_view,
224 EShellViewClass *shell_view_class)
225 {
226 if (!gal_view_collection_loaded (shell_view_class->view_collection))
227 task_shell_view_load_view_collection (shell_view_class);
228
229 g_signal_connect (
230 task_shell_view, "notify::view-id",
231 G_CALLBACK (task_shell_view_notify_view_id_cb), NULL);
232 }
233
234 void
235 e_task_shell_view_private_constructed (ETaskShellView *task_shell_view)
236 {
237 ETaskShellViewPrivate *priv = task_shell_view->priv;
238 ETaskShellContent *task_shell_content;
239 ETaskShellSidebar *task_shell_sidebar;
240 EShell *shell;
241 EShellView *shell_view;
242 EShellBackend *shell_backend;
243 EShellContent *shell_content;
244 EShellSidebar *shell_sidebar;
245 EShellSettings *shell_settings;
246 EShellWindow *shell_window;
247 ETaskTable *task_table;
248 ECalModel *model;
249 ESourceSelector *selector;
250
251 shell_view = E_SHELL_VIEW (task_shell_view);
252 shell_backend = e_shell_view_get_shell_backend (shell_view);
253 shell_content = e_shell_view_get_shell_content (shell_view);
254 shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
255 shell_window = e_shell_view_get_shell_window (shell_view);
256
257 shell = e_shell_window_get_shell (shell_window);
258 shell_settings = e_shell_get_shell_settings (shell);
259
260 e_shell_window_add_action_group (shell_window, "tasks");
261 e_shell_window_add_action_group (shell_window, "tasks-filter");
262
263 /* Cache these to avoid lots of awkward casting. */
264 priv->task_shell_backend = g_object_ref (shell_backend);
265 priv->task_shell_content = g_object_ref (shell_content);
266 priv->task_shell_sidebar = g_object_ref (shell_sidebar);
267
268 task_shell_content = E_TASK_SHELL_CONTENT (shell_content);
269 task_table = e_task_shell_content_get_task_table (task_shell_content);
270 model = e_task_table_get_model (task_table);
271
272 task_shell_sidebar = E_TASK_SHELL_SIDEBAR (shell_sidebar);
273 selector = e_task_shell_sidebar_get_selector (task_shell_sidebar);
274
275 g_signal_connect_object (
276 model, "notify::timezone",
277 G_CALLBACK (e_task_shell_view_update_timezone),
278 task_shell_view, G_CONNECT_SWAPPED);
279
280 g_signal_connect_object (
281 model, "row-appended",
282 G_CALLBACK (task_shell_view_model_row_appended_cb),
283 task_shell_view, G_CONNECT_SWAPPED);
284
285 g_signal_connect_object (
286 task_table, "open-component",
287 G_CALLBACK (e_task_shell_view_open_task),
288 task_shell_view, G_CONNECT_SWAPPED);
289
290 g_signal_connect_object (
291 task_table, "popup-event",
292 G_CALLBACK (task_shell_view_table_popup_event_cb),
293 task_shell_view, G_CONNECT_SWAPPED);
294
295 g_signal_connect_object (
296 task_table, "selection-change",
297 G_CALLBACK (e_task_shell_view_update_sidebar),
298 task_shell_view, G_CONNECT_SWAPPED);
299
300 g_signal_connect_object (
301 task_table, "selection-change",
302 G_CALLBACK (e_shell_view_update_actions),
303 task_shell_view, G_CONNECT_SWAPPED);
304
305 g_signal_connect_object (
306 task_table, "status-message",
307 G_CALLBACK (e_task_shell_view_set_status_message),
308 task_shell_view, G_CONNECT_SWAPPED);
309
310 g_signal_connect_object (
311 model, "model-changed",
312 G_CALLBACK (e_task_shell_view_update_sidebar),
313 task_shell_view, G_CONNECT_SWAPPED);
314
315 g_signal_connect_object (
316 model, "model-rows-deleted",
317 G_CALLBACK (e_task_shell_view_update_sidebar),
318 task_shell_view, G_CONNECT_SWAPPED);
319
320 g_signal_connect_object (
321 model, "model-rows-inserted",
322 G_CALLBACK (e_task_shell_view_update_sidebar),
323 task_shell_view, G_CONNECT_SWAPPED);
324
325 g_signal_connect_object (
326 task_shell_sidebar, "client-added",
327 G_CALLBACK (task_shell_view_selector_client_added_cb),
328 task_shell_view, G_CONNECT_SWAPPED);
329
330 g_signal_connect_object (
331 task_shell_sidebar, "client-removed",
332 G_CALLBACK (task_shell_view_selector_client_removed_cb),
333 task_shell_view, G_CONNECT_SWAPPED);
334
335 g_signal_connect_object (
336 task_shell_sidebar, "status-message",
337 G_CALLBACK (e_task_shell_view_set_status_message),
338 task_shell_view, G_CONNECT_SWAPPED);
339
340 g_signal_connect_object (
341 selector, "popup-event",
342 G_CALLBACK (task_shell_view_selector_popup_event_cb),
343 task_shell_view, G_CONNECT_SWAPPED);
344
345 g_signal_connect_object (
346 selector, "primary-selection-changed",
347 G_CALLBACK (e_shell_view_update_actions),
348 task_shell_view, G_CONNECT_SWAPPED);
349
350 e_categories_add_change_hook (
351 (GHookFunc) e_task_shell_view_update_search_filter,
352 task_shell_view);
353
354 /* Listen for configuration changes. */
355 g_object_bind_property (
356 shell_settings, "cal-confirm-purge",
357 task_shell_view, "confirm-purge",
358 G_BINDING_BIDIRECTIONAL |
359 G_BINDING_SYNC_CREATE);
360
361 /* Keep the ECalModel in sync with the sidebar. */
362 g_object_bind_property (
363 shell_sidebar, "default-client",
364 model, "default-client",
365 G_BINDING_SYNC_CREATE);
366
367 /* Hide Completed Tasks (enable/units/value) */
368 g_signal_connect_object (
369 shell_settings, "notify::cal-hide-completed-tasks",
370 G_CALLBACK (task_shell_view_schedule_process_completed_tasks),
371 task_shell_view, G_CONNECT_SWAPPED);
372 g_signal_connect_object (
373 shell_settings, "notify::cal-hide-completed-tasks-units",
374 G_CALLBACK (task_shell_view_schedule_process_completed_tasks),
375 task_shell_view, G_CONNECT_SWAPPED);
376 g_signal_connect_object (
377 shell_settings, "notify::cal-hide-completed-tasks-value",
378 G_CALLBACK (task_shell_view_schedule_process_completed_tasks),
379 task_shell_view, G_CONNECT_SWAPPED);
380
381 e_task_shell_view_actions_init (task_shell_view);
382 e_task_shell_view_update_sidebar (task_shell_view);
383 e_task_shell_view_update_search_filter (task_shell_view);
384 e_task_shell_view_update_timezone (task_shell_view);
385
386 /* Call this when everything is ready, like actions in
387 * action groups and such. */
388 task_shell_view_update_timeout_cb (task_shell_view);
389 priv->update_timeout = g_timeout_add_full (
390 G_PRIORITY_LOW, 60000, (GSourceFunc)
391 task_shell_view_update_timeout_cb,
392 task_shell_view, NULL);
393 }
394
395 void
396 e_task_shell_view_private_dispose (ETaskShellView *task_shell_view)
397 {
398 ETaskShellViewPrivate *priv = task_shell_view->priv;
399
400 DISPOSE (priv->task_shell_backend);
401 DISPOSE (priv->task_shell_content);
402 DISPOSE (priv->task_shell_sidebar);
403
404 if (task_shell_view->priv->activity != NULL) {
405 /* XXX Activity is not cancellable. */
406 e_activity_set_state (
407 task_shell_view->priv->activity,
408 E_ACTIVITY_COMPLETED);
409 g_object_unref (task_shell_view->priv->activity);
410 task_shell_view->priv->activity = NULL;
411 }
412
413 if (priv->update_timeout > 0) {
414 g_source_remove (priv->update_timeout);
415 priv->update_timeout = 0;
416 }
417
418 if (priv->update_completed_timeout > 0) {
419 g_source_remove (priv->update_completed_timeout);
420 priv->update_completed_timeout = 0;
421 }
422 }
423
424 void
425 e_task_shell_view_private_finalize (ETaskShellView *task_shell_view)
426 {
427 /* XXX Nothing to do? */
428 }
429
430 void
431 e_task_shell_view_open_task (ETaskShellView *task_shell_view,
432 ECalModelComponent *comp_data)
433 {
434 EShell *shell;
435 EShellView *shell_view;
436 EShellWindow *shell_window;
437 ESourceRegistry *registry;
438 CompEditor *editor;
439 CompEditorFlags flags = 0;
440 ECalComponent *comp;
441 icalcomponent *clone;
442 icalproperty *prop;
443 const gchar *uid;
444
445 g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view));
446 g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data));
447
448 shell_view = E_SHELL_VIEW (task_shell_view);
449 shell_window = e_shell_view_get_shell_window (shell_view);
450 shell = e_shell_window_get_shell (shell_window);
451
452 registry = e_shell_get_registry (shell);
453
454 uid = icalcomponent_get_uid (comp_data->icalcomp);
455 editor = comp_editor_find_instance (uid);
456
457 if (editor != NULL)
458 goto exit;
459
460 comp = e_cal_component_new ();
461 clone = icalcomponent_new_clone (comp_data->icalcomp);
462 e_cal_component_set_icalcomponent (comp, clone);
463
464 prop = icalcomponent_get_first_property (
465 comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY);
466 if (prop != NULL)
467 flags |= COMP_EDITOR_IS_ASSIGNED;
468
469 if (itip_organizer_is_user (registry, comp, comp_data->client))
470 flags |= COMP_EDITOR_USER_ORG;
471
472 if (!e_cal_component_has_attendees (comp))
473 flags |= COMP_EDITOR_USER_ORG;
474
475 editor = task_editor_new (comp_data->client, shell, flags);
476 comp_editor_edit_comp (editor, comp);
477
478 g_object_unref (comp);
479
480 if (flags & COMP_EDITOR_IS_ASSIGNED)
481 task_editor_show_assignment (TASK_EDITOR (editor));
482
483 exit:
484 gtk_window_present (GTK_WINDOW (editor));
485 }
486
487 void
488 e_task_shell_view_delete_completed (ETaskShellView *task_shell_view)
489 {
490 ETaskShellSidebar *task_shell_sidebar;
491 GList *list, *iter;
492 const gchar *sexp;
493
494 g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view));
495
496 sexp = "(is-completed?)";
497
498 task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
499 list = e_task_shell_sidebar_get_clients (task_shell_sidebar);
500
501 e_task_shell_view_set_status_message (
502 task_shell_view, _("Expunging"), -1.0);
503
504 for (iter = list; iter != NULL; iter = iter->next) {
505 ECalClient *client = E_CAL_CLIENT (iter->data);
506 GSList *objects, *obj;
507 GError *error = NULL;
508
509 if (e_client_is_readonly (E_CLIENT (client)))
510 continue;
511
512 e_cal_client_get_object_list_sync (
513 client, sexp, &objects, NULL, &error);
514
515 if (error != NULL) {
516 g_warning (
517 "%s: Failed to get object list: %s",
518 G_STRFUNC, error->message);
519 g_clear_error (&error);
520 continue;
521 }
522
523 for (obj = objects; obj != NULL; obj = obj->next) {
524 icalcomponent *component = obj->data;
525 const gchar *uid;
526
527 uid = icalcomponent_get_uid (component);
528
529 e_cal_client_remove_object_sync (
530 client, uid, NULL,
531 CALOBJ_MOD_THIS, NULL, &error);
532
533 if (error != NULL) {
534 g_warning (
535 "%s: Failed to remove object: %s",
536 G_STRFUNC, error->message);
537 g_clear_error (&error);
538 }
539 }
540
541 e_cal_client_free_icalcomp_slist (objects);
542 }
543
544 e_task_shell_view_set_status_message (task_shell_view, NULL, -1.0);
545 }
546
547 void
548 e_task_shell_view_set_status_message (ETaskShellView *task_shell_view,
549 const gchar *status_message,
550 gdouble percent)
551 {
552 EActivity *activity;
553 EShellView *shell_view;
554 EShellBackend *shell_backend;
555
556 g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view));
557
558 activity = task_shell_view->priv->activity;
559 shell_view = E_SHELL_VIEW (task_shell_view);
560 shell_backend = e_shell_view_get_shell_backend (shell_view);
561
562 if (status_message == NULL || *status_message == '\0') {
563 if (activity != NULL) {
564 e_activity_set_state (activity, E_ACTIVITY_COMPLETED);
565 g_object_unref (activity);
566 activity = NULL;
567 }
568
569 } else if (activity == NULL) {
570 activity = e_activity_new ();
571 e_activity_set_percent (activity, percent);
572 e_activity_set_text (activity, status_message);
573 e_shell_backend_add_activity (shell_backend, activity);
574
575 } else {
576 e_activity_set_percent (activity, percent);
577 e_activity_set_text (activity, status_message);
578 }
579
580 task_shell_view->priv->activity = activity;
581 }
582
583 void
584 e_task_shell_view_update_sidebar (ETaskShellView *task_shell_view)
585 {
586 ETaskShellContent *task_shell_content;
587 EShellView *shell_view;
588 EShellSidebar *shell_sidebar;
589 ETaskTable *task_table;
590 ECalModel *model;
591 GString *string;
592 const gchar *format;
593 gint n_rows;
594 gint n_selected;
595
596 shell_view = E_SHELL_VIEW (task_shell_view);
597 shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
598
599 task_shell_content = task_shell_view->priv->task_shell_content;
600 task_table = e_task_shell_content_get_task_table (task_shell_content);
601
602 model = e_task_table_get_model (task_table);
603
604 n_rows = e_table_model_row_count (E_TABLE_MODEL (model));
605 n_selected = e_table_selected_count (E_TABLE (task_table));
606
607 string = g_string_sized_new (64);
608
609 format = ngettext ("%d task", "%d tasks", n_rows);
610 g_string_append_printf (string, format, n_rows);
611
612 if (n_selected > 0) {
613 format = _("%d selected");
614 g_string_append_len (string, ", ", 2);
615 g_string_append_printf (string, format, n_selected);
616 }
617
618 e_shell_sidebar_set_secondary_text (shell_sidebar, string->str);
619
620 g_string_free (string, TRUE);
621 }
622
623 void
624 e_task_shell_view_update_timezone (ETaskShellView *task_shell_view)
625 {
626 ETaskShellContent *task_shell_content;
627 ETaskShellSidebar *task_shell_sidebar;
628 icaltimezone *timezone;
629 ECalModel *model;
630 GList *clients, *iter;
631
632 task_shell_content = task_shell_view->priv->task_shell_content;
633 model = e_task_shell_content_get_task_model (task_shell_content);
634 timezone = e_cal_model_get_timezone (model);
635
636 task_shell_sidebar = task_shell_view->priv->task_shell_sidebar;
637 clients = e_task_shell_sidebar_get_clients (task_shell_sidebar);
638
639 for (iter = clients; iter != NULL; iter = iter->next) {
640 ECalClient *client = iter->data;
641
642 if (e_client_is_opened (E_CLIENT (client)))
643 e_cal_client_set_default_timezone (client, timezone);
644 }
645
646 g_list_free (clients);
647 }