Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
task-page.c:2111:33 | clang-analyzer | Dereference of null pointer (loaded from variable 'backend_address') | ||
task-page.c:2111:33 | clang-analyzer | Dereference of null pointer (loaded from variable 'backend_address') |
1 /*
2 * Evolution calendar - Main page of the task editor dialog
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 * Authors:
19 * Federico Mena-Quintero <federico@ximian.com>
20 * Miguel de Icaza <miguel@ximian.com>
21 * Seth Alves <alves@hungry.com>
22 * JP Rosevear <jpr@ximian.com>
23 *
24 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
25 *
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <string.h>
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35 #include <gdk/gdkkeysyms.h>
36
37 #include <libedataserverui/libedataserverui.h>
38
39 #include <misc/e-dateedit.h>
40 #include <misc/e-spell-entry.h>
41 #include <misc/e-buffer-tagger.h>
42
43 #include <e-util/e-categories-config.h>
44 #include <e-util/e-dialog-utils.h>
45 #include <e-util/e-util-private.h>
46 #include <e-util/e-util.h>
47
48 #include "../e-meeting-attendee.h"
49 #include "../e-meeting-list-view.h"
50 #include "../e-meeting-store.h"
51 #include "../e-timezone-entry.h"
52
53 #include "comp-editor.h"
54 #include "comp-editor-util.h"
55 #include "e-send-options-utils.h"
56 #include "task-page.h"
57
58 #define TASK_PAGE_GET_PRIVATE(obj) \
59 (G_TYPE_INSTANCE_GET_PRIVATE \
60 ((obj), TYPE_TASK_PAGE, TaskPagePrivate))
61
62 struct _TaskPagePrivate {
63 GtkBuilder *builder;
64
65 /* Widgets from the UI file */
66 GtkWidget *main;
67
68 gchar **address_strings;
69 gchar *fallback_address;
70 EMeetingAttendee *ia;
71 gchar *user_add;
72 ECalComponent *comp;
73
74 /* For meeting/event */
75 GtkWidget *calendar_label;
76 GtkWidget *org_cal_label;
77 GtkWidget *attendee_box;
78
79 /* Lists of attendees */
80 GPtrArray *deleted_attendees;
81
82 /* Generic informative messages placeholder */
83 GtkWidget *info_hbox;
84 GtkWidget *info_icon;
85 GtkWidget *info_string;
86
87 GtkWidget *summary;
88 GtkWidget *summary_label;
89
90 GtkWidget *due_date;
91 GtkWidget *start_date;
92 GtkWidget *timezone;
93 GtkWidget *timezone_label;
94
95 GtkWidget *description;
96
97 GtkWidget *categories_btn;
98 GtkWidget *categories;
99
100 GtkWidget *source_combo_box;
101
102 /* Meeting related items */
103 GtkWidget *list_box;
104 GtkWidget *organizer_table;
105 GtkWidget *organizer;
106 GtkWidget *add;
107 GtkWidget *remove;
108 GtkWidget *edit;
109 GtkWidget *invite;
110 GtkWidget *attendees_label;
111
112 /* ListView stuff */
113 ECalClient *client;
114 EMeetingStore *meeting_store;
115 EMeetingListView *list_view;
116 gint row;
117
118 /* For handling who the organizer is */
119 gboolean user_org;
120 gboolean existing;
121
122 gboolean sendoptions_shown;
123 gboolean is_assignment;
124
125 ESendOptionsDialog *sod;
126
127 GCancellable *open_cancellable;
128 };
129
130 static const gint classification_map[] = {
131 E_CAL_COMPONENT_CLASS_PUBLIC,
132 E_CAL_COMPONENT_CLASS_PRIVATE,
133 E_CAL_COMPONENT_CLASS_CONFIDENTIAL,
134 -1
135 };
136
137 static gboolean task_page_fill_timezones (CompEditorPage *page, GHashTable *timezones);
138 static void task_page_select_organizer (TaskPage *tpage, const gchar *backend_address);
139 static void set_subscriber_info_string (TaskPage *tpage, const gchar *backend_address);
140
141 G_DEFINE_TYPE (TaskPage, task_page, TYPE_COMP_EDITOR_PAGE)
142
143 static gboolean
144 get_current_identity (TaskPage *page,
145 gchar **name,
146 gchar **mailto)
147 {
148 EShell *shell;
149 CompEditor *editor;
150 ESourceRegistry *registry;
151 GList *list, *iter;
152 GtkWidget *entry;
153 const gchar *extension_name;
154 const gchar *text;
155 gboolean match = FALSE;
156
157 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
158 shell = comp_editor_get_shell (editor);
159
160 entry = gtk_bin_get_child (GTK_BIN (page->priv->organizer));
161 text = gtk_entry_get_text (GTK_ENTRY (entry));
162
163 if (text == NULL || *text == '\0')
164 return FALSE;
165
166 registry = e_shell_get_registry (shell);
167 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
168
169 list = e_source_registry_list_sources (registry, extension_name);
170
171 for (iter = list; !match && iter != NULL; iter = g_list_next (iter)) {
172 ESource *source = E_SOURCE (iter->data);
173 ESourceMailIdentity *extension;
174 const gchar *id_name;
175 const gchar *id_address;
176 gchar *identity;
177
178 extension = e_source_get_extension (source, extension_name);
179
180 id_name = e_source_mail_identity_get_name (extension);
181 id_address = e_source_mail_identity_get_address (extension);
182
183 if (id_name == NULL || id_address == NULL)
184 continue;
185
186 identity = g_strdup_printf ("%s <%s>", id_name, id_address);
187 match = (g_ascii_strcasecmp (text, identity) == 0);
188 g_free (identity);
189
190 if (match && name != NULL)
191 *name = g_strdup (id_name);
192
193 if (match && mailto != NULL)
194 *mailto = g_strdup_printf ("MAILTO:%s", id_address);
195 }
196
197 g_list_free_full (list, (GDestroyNotify) g_object_unref);
198
199 return match;
200 }
201
202 /* Fills the widgets with default values */
203 static void
204 clear_widgets (TaskPage *tpage)
205 {
206 TaskPagePrivate *priv = tpage->priv;
207 CompEditor *editor;
208
209 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
210
211 /* Summary, description */
212 gtk_entry_set_text (GTK_ENTRY (priv->summary), "");
213 gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)), "", 0);
214 e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->description));
215
216 /* Start, due times - both set to None */
217 e_date_edit_set_time (E_DATE_EDIT (priv->start_date), -1);
218 e_date_edit_set_time (E_DATE_EDIT (priv->due_date), -1);
219
220 /* Classification */
221 comp_editor_set_classification (editor, E_CAL_COMPONENT_CLASS_PUBLIC);
222
223 /* Categories */
224 gtk_entry_set_text (GTK_ENTRY (priv->categories), "");
225 }
226
227 static gboolean
228 date_in_past (TaskPage *tpage,
229 EDateEdit *date)
230 {
231 struct icaltimetype tt = icaltime_null_time ();
232
233 if (!e_date_edit_get_date (date, &tt.year, &tt.month, &tt.day))
234 return FALSE;
235
236 if (e_date_edit_get_time_of_day (date, &tt.hour, &tt.minute))
237 tt.zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (tpage->priv->timezone));
238 else
239 tt.is_date = TRUE;
240
241 return comp_editor_test_time_in_the_past (tt);
242 }
243
244 /* returns whether changed info text */
245 static gboolean
246 check_starts_in_the_past (TaskPage *tpage)
247 {
248 TaskPagePrivate *priv;
249 gboolean start_in_past, due_in_past;
250
251 if ((comp_editor_get_flags (comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage))) & COMP_EDITOR_NEW_ITEM) == 0)
252 return FALSE;
253
254 priv = tpage->priv;
255 start_in_past = date_in_past (tpage, E_DATE_EDIT (priv->start_date));
256 due_in_past = date_in_past (tpage, E_DATE_EDIT (priv->due_date));
257
258 if (start_in_past || due_in_past) {
259 gchar *tmp = g_strconcat (
260 "<b>", start_in_past ? _("Task's start date is in the past") : "",
261 start_in_past && due_in_past ? "\n" : "", due_in_past ? _("Task's due date is in the past") : "", "</b>", NULL);
262 task_page_set_info_string (tpage, GTK_STOCK_DIALOG_WARNING, tmp);
263 g_free (tmp);
264 } else {
265 task_page_set_info_string (tpage, NULL, NULL);
266 }
267
268 return TRUE;
269 }
270
271 static void
272 sensitize_widgets (TaskPage *tpage)
273 {
274 TaskPagePrivate *priv = tpage->priv;
275 CompEditor *editor;
276 CompEditorFlags flags;
277 ECalClient *client;
278 GtkActionGroup *action_group;
279 GtkAction *action;
280 gboolean read_only, sens = TRUE, sensitize;
281
282 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
283 client = comp_editor_get_client (editor);
284 flags = comp_editor_get_flags (editor);
285
286 read_only = e_client_is_readonly (E_CLIENT (client));
287
288 if (flags & COMP_EDITOR_IS_ASSIGNED)
289 sens = flags & COMP_EDITOR_USER_ORG;
290
291 sensitize = (!read_only && sens);
292
293 if (read_only) {
294 gchar *tmp = g_strconcat ("<b>", _("Task cannot be edited, because the selected task list is read only"), "</b>", NULL);
295 task_page_set_info_string (tpage, GTK_STOCK_DIALOG_INFO, tmp);
296 g_free (tmp);
297 } else if (!sens) {
298 gchar *tmp = g_strconcat ("<b>", _("Task cannot be fully edited, because you are not the organizer"), "</b>", NULL);
299 task_page_set_info_string (tpage, GTK_STOCK_DIALOG_INFO, tmp);
300 g_free (tmp);
301 } else if ((flags & COMP_EDITOR_IS_ASSIGNED) != 0 && e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
302 gchar *tmp = g_strconcat ("<b>", _("Task cannot be edited, because the selected task list does not support assigned tasks"), "</b>", NULL);
303 task_page_set_info_string (tpage, GTK_STOCK_DIALOG_INFO, tmp);
304 g_free (tmp);
305 sens = FALSE;
306 read_only = TRUE;
307 } else if (!check_starts_in_the_past (tpage)) {
308 task_page_set_info_string (tpage, NULL, NULL);
309 }
310
311 /* The list of organizers is set to be non-editable. Otherwise any
312 * change in the displayed list causes an 'Account not found' error.
313 */
314 gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->organizer))), FALSE);
315
316 gtk_editable_set_editable (GTK_EDITABLE (priv->summary), !read_only);
317 gtk_widget_set_sensitive (priv->due_date, !read_only);
318 gtk_widget_set_sensitive (priv->start_date, !read_only);
319 gtk_widget_set_sensitive (priv->timezone, !read_only);
320 gtk_widget_set_sensitive (priv->description, !read_only);
321 gtk_widget_set_sensitive (priv->categories_btn, !read_only);
322 gtk_editable_set_editable (GTK_EDITABLE (priv->categories), !read_only);
323
324 gtk_widget_set_sensitive (priv->organizer, !read_only);
325 gtk_widget_set_sensitive (priv->add, (!read_only && sens));
326 gtk_widget_set_sensitive (priv->edit, (!read_only && sens));
327 e_meeting_list_view_set_editable (priv->list_view, (!read_only && sens));
328 gtk_widget_set_sensitive (priv->remove, (!read_only && sens));
329 gtk_widget_set_sensitive (priv->invite, (!read_only && sens));
330 gtk_widget_set_sensitive (GTK_WIDGET (priv->list_view), !read_only);
331
332 action_group = comp_editor_get_action_group (editor, "editable");
333 gtk_action_group_set_sensitive (action_group, !read_only);
334
335 action_group = comp_editor_get_action_group (editor, "individual");
336 gtk_action_group_set_sensitive (action_group, sensitize);
337
338 action = comp_editor_get_action (editor, "send-options");
339 gtk_action_set_sensitive (action, sensitize);
340
341 if (!priv->is_assignment) {
342 gtk_widget_hide (priv->calendar_label);
343 gtk_widget_hide (priv->list_box);
344 gtk_widget_hide (priv->attendee_box);
345 gtk_widget_hide (priv->organizer);
346 gtk_widget_hide (priv->invite);
347 gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->org_cal_label), _("_List:"));
348 gtk_label_set_mnemonic_widget (GTK_LABEL (priv->org_cal_label), priv->source_combo_box);
349 } else {
350 gtk_widget_show (priv->invite);
351 gtk_widget_show (priv->calendar_label);
352 gtk_widget_show (priv->list_box);
353 gtk_widget_show (priv->attendee_box);
354 gtk_widget_show (priv->organizer);
355 gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->org_cal_label), _("Organi_zer:"));
356 gtk_label_set_mnemonic_widget (GTK_LABEL (priv->org_cal_label), priv->organizer);
357 }
358 }
359
360 static void
361 set_attendees (ECalComponent *comp,
362 const GPtrArray *attendees)
363 {
364 GSList *comp_attendees = NULL, *l;
365 gint i;
366
367 for (i = 0; i < attendees->len; i++) {
368 EMeetingAttendee *ia = g_ptr_array_index (attendees, i);
369 ECalComponentAttendee *ca;
370
371 ca = e_meeting_attendee_as_e_cal_component_attendee (ia);
372
373 comp_attendees = g_slist_prepend (comp_attendees, ca);
374
375 }
376 comp_attendees = g_slist_reverse (comp_attendees);
377
378 e_cal_component_set_attendee_list (comp, comp_attendees);
379
380 for (l = comp_attendees; l != NULL; l = l->next)
381 g_free (l->data);
382 g_slist_free (comp_attendees);
383 }
384
385 static void
386 organizer_changed_cb (GtkEntry *entry,
387 TaskPage *tpage)
388 {
389 gchar *name;
390 gchar *mailto;
391
392 g_return_if_fail (GTK_IS_ENTRY (entry));
393 g_return_if_fail (IS_TASK_PAGE (tpage));
394
395 if (!tpage->priv->ia)
396 return;
397
398 if (!get_current_identity (tpage, &name, &mailto))
399 return;
400
401 /* XXX EMeetingAttendee takes ownership of the strings. */
402 e_meeting_attendee_set_cn (tpage->priv->ia, name);
403 e_meeting_attendee_set_address (tpage->priv->ia, mailto);
404 }
405
406 static void
407 task_page_dispose (GObject *object)
408 {
409 TaskPagePrivate *priv;
410
411 priv = TASK_PAGE_GET_PRIVATE (object);
412
413 if (priv->open_cancellable) {
414 g_cancellable_cancel (priv->open_cancellable);
415 g_object_unref (priv->open_cancellable);
416 priv->open_cancellable = NULL;
417 }
418
419 if (priv->main != NULL) {
420 g_object_unref (priv->main);
421 priv->main = NULL;
422 }
423
424 if (priv->builder != NULL) {
425 g_object_unref (priv->builder);
426 priv->builder = NULL;
427 }
428
429 if (priv->sod != NULL) {
430 g_object_unref (priv->sod);
431 priv->sod = NULL;
432 }
433
434 if (priv->comp != NULL) {
435 g_object_unref (priv->comp);
436 priv->comp = NULL;
437 }
438
439 /* Chain up to parent's dispose() method. */
440 G_OBJECT_CLASS (task_page_parent_class)->dispose (object);
441 }
442
443 static void
444 task_page_finalize (GObject *object)
445 {
446 TaskPagePrivate *priv;
447
448 priv = TASK_PAGE_GET_PRIVATE (object);
449
450 g_strfreev (priv->address_strings);
451 g_free (priv->fallback_address);
452
453 g_ptr_array_foreach (
454 priv->deleted_attendees, (GFunc) g_object_unref, NULL);
455 g_ptr_array_free (priv->deleted_attendees, TRUE);
456
457 /* Chain up to parent's finalize() method. */
458 G_OBJECT_CLASS (task_page_parent_class)->finalize (object);
459 }
460
461 static GtkWidget *
462 task_page_get_widget (CompEditorPage *page)
463 {
464 TaskPage *tpage;
465 TaskPagePrivate *priv;
466
467 tpage = TASK_PAGE (page);
468 priv = tpage->priv;
469
470 return priv->main;
471 }
472
473 static void
474 task_page_focus_main_widget (CompEditorPage *page)
475 {
476 TaskPage *tpage;
477 TaskPagePrivate *priv;
478
479 tpage = TASK_PAGE (page);
480 priv = tpage->priv;
481
482 gtk_widget_grab_focus (priv->summary);
483 }
484
485 static gboolean
486 task_page_fill_widgets (CompEditorPage *page,
487 ECalComponent *comp)
488 {
489 TaskPage *tpage;
490 TaskPagePrivate *priv;
491 ECalComponentText text;
492 ECalComponentDateTime d;
493 ECalComponentClassification cl;
494 ESourceRegistry *registry;
495 CompEditor *editor;
496 CompEditorFlags flags;
497 GtkAction *action;
498 ECalClient *client;
499 EShell *shell;
500 GSList *l;
501 icalcomponent *icalcomp;
502 const gchar *categories, *uid;
503 icaltimezone *zone, *default_zone;
504 gchar *backend_addr = NULL;
505 gboolean active;
506
507 tpage = TASK_PAGE (page);
508 priv = tpage->priv;
509
510 editor = comp_editor_page_get_editor (page);
511 client = comp_editor_get_client (editor);
512 flags = comp_editor_get_flags (editor);
513 shell = comp_editor_get_shell (editor);
514
515 registry = e_shell_get_registry (shell);
516
517 /* Clean out old data */
518 if (priv->comp != NULL)
519 g_object_unref (priv->comp);
520 priv->comp = NULL;
521
522 g_ptr_array_foreach (
523 priv->deleted_attendees, (GFunc) g_object_unref, NULL);
524 g_ptr_array_set_size (priv->deleted_attendees, 0);
525
526 /* Component for cancellation */
527 priv->comp = e_cal_component_clone (comp);
528 comp_editor_copy_new_attendees (priv->comp, comp);
529
530 /* Clean the screen */
531 clear_widgets (tpage);
532
533 priv->user_add = itip_get_comp_attendee (
534 registry, comp, client);
535
536 /* Summary, description(s) */
537 e_cal_component_get_summary (comp, &text);
538 if (text.value != NULL)
539 gtk_entry_set_text (GTK_ENTRY (priv->summary), text.value);
540 else
541 gtk_entry_set_text (GTK_ENTRY (priv->summary), "");
542
543 e_cal_component_get_description_list (comp, &l);
544 if (l && l->data) {
545 ECalComponentText *dtext;
546
547 dtext = l->data;
548 gtk_text_buffer_set_text (
549 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)),
550 dtext->value ? dtext->value : "", -1);
551 } else {
552 gtk_text_buffer_set_text (
553 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description)),
554 "", 0);
555 }
556 e_cal_component_free_text_list (l);
557 e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->description));
558
559 default_zone = comp_editor_get_timezone (editor);
560
561 /* Due Date. */
562 e_cal_component_get_due (comp, &d);
563 zone = NULL;
564 if (d.value) {
565 struct icaltimetype *due_tt = d.value;
566 e_date_edit_set_date (
567 E_DATE_EDIT (priv->due_date),
568 due_tt->year, due_tt->month,
569 due_tt->day);
570 e_date_edit_set_time_of_day (E_DATE_EDIT (priv->due_date), -1, -1);
571 } else {
572 e_date_edit_set_time (E_DATE_EDIT (priv->due_date), -1);
573
574 /* If no time is set, we use the default timezone, so the
575 * user usually doesn't have to set this when they set the
576 * date. */
577 zone = NULL;
578 }
579
580 /* Note that if we are creating a new task, the timezones may not be
581 * on the server, so we try to get the builtin timezone with the TZID
582 * first. */
583 if (!zone && d.tzid) {
584 GError *error = NULL;
585
586 e_cal_client_get_timezone_sync (
587 client, d.tzid, &zone, NULL, &error);
588
589 if (error != NULL) {
590 /* FIXME: Handle error better. */
591 g_warning (
592 "Couldn't get timezone '%s' from server: %s",
593 d.tzid ? d.tzid : "", error->message);
594 g_error_free (error);
595 }
596 }
597
598 e_timezone_entry_set_timezone (
599 E_TIMEZONE_ENTRY (priv->timezone),
600 zone ? zone : default_zone);
601
602 action = comp_editor_get_action (editor, "view-time-zone");
603 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
604 task_page_set_show_timezone (tpage, active);
605
606 if (!(flags & COMP_EDITOR_NEW_ITEM) && !zone) {
607 GtkAction *action;
608
609 task_page_set_show_timezone (tpage, FALSE);
610 action = comp_editor_get_action (editor, "view-time-zone");
611 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
612 }
613
614 e_cal_component_free_datetime (&d);
615
616 /* Start Date. */
617 e_cal_component_get_dtstart (comp, &d);
618 zone = NULL;
619 if (d.value) {
620 struct icaltimetype *start_tt = d.value;
621 e_date_edit_set_date (
622 E_DATE_EDIT (priv->start_date),
623 start_tt->year, start_tt->month,
624 start_tt->day);
625 e_date_edit_set_time_of_day (E_DATE_EDIT (priv->start_date), -1, -1);
626 } else {
627 e_date_edit_set_time (E_DATE_EDIT (priv->start_date), -1);
628 }
629
630 e_cal_component_free_datetime (&d);
631
632 /* Classification. */
633 e_cal_component_get_classification (comp, &cl);
634 comp_editor_set_classification (editor, cl);
635
636 e_cal_component_get_uid (comp, &uid);
637 if (e_cal_client_get_object_sync (client, uid, NULL, &icalcomp, NULL, NULL)) {
638 icalcomponent_free (icalcomp);
639 task_page_hide_options (tpage);
640 }
641
642 /* Categories */
643 e_cal_component_get_categories (comp, &categories);
644 if (categories != NULL)
645 gtk_entry_set_text (GTK_ENTRY (priv->categories), categories);
646 else
647 gtk_entry_set_text (GTK_ENTRY (priv->categories), "");
648
649 /* Source */
650 e_source_combo_box_set_active (
651 E_SOURCE_COMBO_BOX (priv->source_combo_box),
652 e_client_get_source (E_CLIENT (client)));
653
654 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
655 set_subscriber_info_string (tpage, backend_addr);
656
657 if (priv->is_assignment) {
658 ECalComponentOrganizer organizer;
659 gchar *name = NULL;
660 gchar *mailto = NULL;
661
662 priv->user_add = itip_get_comp_attendee (
663 registry, comp, client);
664
665 /* Organizer strings */
666 task_page_select_organizer (tpage, backend_addr);
667
668 /* If there is an existing organizer show it properly */
669 if (e_cal_component_has_organizer (comp)) {
670 e_cal_component_get_organizer (comp, &organizer);
671 if (organizer.value != NULL) {
672 const gchar *strip = itip_strip_mailto (organizer.value);
673 gchar *string;
674
675 if (itip_organizer_is_user (registry, comp, client) ||
676 itip_sentby_is_user (registry, comp, client)) {
677 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS))
678 priv->user_org = TRUE;
679 } else {
680 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS))
681 gtk_widget_set_sensitive (priv->invite, FALSE);
682 gtk_widget_set_sensitive (priv->add, FALSE);
683 gtk_widget_set_sensitive (priv->edit, FALSE);
684 gtk_widget_set_sensitive (priv->remove, FALSE);
685 priv->user_org = FALSE;
686 }
687
688 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_ORGANIZER) && (flags & COMP_EDITOR_DELEGATE))
689 string = g_strdup (priv->user_add);
690 else if (organizer.cn != NULL)
691 string = g_strdup_printf ("%s <%s>", organizer.cn, strip);
692 else
693 string = g_strdup (strip);
694
695 g_signal_handlers_block_by_func (gtk_bin_get_child (GTK_BIN (priv->organizer)), organizer_changed_cb, tpage);
696
697 if (!priv->user_org) {
698 GtkComboBox *combo_box;
699 GtkListStore *list_store;
700 GtkTreeModel *model;
701 GtkTreeIter iter;
702
703 combo_box = GTK_COMBO_BOX (priv->organizer);
704 model = gtk_combo_box_get_model (combo_box);
705 list_store = GTK_LIST_STORE (model);
706
707 gtk_list_store_clear (list_store);
708 gtk_list_store_append (list_store, &iter);
709 gtk_list_store_set (list_store, &iter, 0, string, -1);
710 gtk_combo_box_set_active (combo_box, 0);
711 gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->organizer))), FALSE);
712 } else {
713 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->organizer))), string);
714 }
715
716 g_signal_handlers_unblock_by_func (gtk_bin_get_child (GTK_BIN (priv->organizer)), organizer_changed_cb, tpage);
717
718 g_free (string);
719 priv->existing = TRUE;
720 }
721 } else if (get_current_identity (tpage, &name, &mailto)) {
722 EMeetingAttendee *attendee;
723 gchar *backend_mailto = NULL;
724
725 if (backend_addr != NULL && *backend_addr != '\0') {
726 backend_mailto = g_strdup_printf (
727 "MAILTO:%s", backend_addr);
728 if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) {
729 g_free (backend_mailto);
730 backend_mailto = NULL;
731 }
732 }
733
734 attendee =
735 e_meeting_store_add_attendee_with_defaults (
736 priv->meeting_store);
737 priv->ia = g_object_ref (attendee);
738
739 if (backend_mailto == NULL) {
740 e_meeting_attendee_set_cn (attendee, name);
741 e_meeting_attendee_set_address (attendee, mailto);
742 name = mailto = NULL;
743 } else {
744 e_meeting_attendee_set_address (attendee, backend_mailto);
745 e_meeting_attendee_set_sentby (attendee, mailto);
746 backend_mailto = mailto = NULL;
747 }
748
749 if (client && e_cal_client_check_organizer_must_accept (client))
750 e_meeting_attendee_set_status (
751 attendee, ICAL_PARTSTAT_NEEDSACTION);
752 else
753 e_meeting_attendee_set_status (
754 attendee, ICAL_PARTSTAT_ACCEPTED);
755
756 e_meeting_list_view_add_attendee_to_name_selector (
757 E_MEETING_LIST_VIEW (priv->list_view), attendee);
758
759 g_free (backend_mailto);
760 }
761
762 g_free (mailto);
763 g_free (name);
764 }
765
766 g_free (backend_addr);
767
768 sensitize_widgets (tpage);
769
770 return TRUE;
771 }
772
773 static gboolean
774 task_page_fill_component (CompEditorPage *page,
775 ECalComponent *comp)
776 {
777 TaskPage *tpage;
778 TaskPagePrivate *priv;
779 ECalComponentClassification classification;
780 ECalComponentDateTime date;
781 CompEditor *editor;
782 CompEditorFlags flags;
783 ECalClient *client;
784 struct icaltimetype start_tt, due_tt;
785 gchar *cat, *str;
786 gboolean start_date_set, due_date_set;
787 GtkTextBuffer *text_buffer;
788 GtkTextIter text_iter_start, text_iter_end;
789
790 tpage = TASK_PAGE (page);
791 priv = tpage->priv;
792 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description));
793
794 editor = comp_editor_page_get_editor (page);
795 client = comp_editor_get_client (editor);
796 flags = comp_editor_get_flags (editor);
797
798 /* Summary. */
799
800 str = gtk_editable_get_chars (GTK_EDITABLE (priv->summary), 0, -1);
801 if (str == NULL || *str == '\0')
802 e_cal_component_set_summary (comp, NULL);
803 else {
804 ECalComponentText text;
805
806 text.value = str;
807 text.altrep = NULL;
808
809 e_cal_component_set_summary (comp, &text);
810 }
811
812 g_free (str);
813
814 /* Description */
815
816 gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start);
817 gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
818 str = gtk_text_buffer_get_text (text_buffer, &text_iter_start, &text_iter_end, FALSE);
819
820 if (!str || strlen (str) == 0)
821 e_cal_component_set_description_list (comp, NULL);
822 else {
823 GSList l;
824 ECalComponentText text;
825
826 text.value = str;
827 text.altrep = NULL;
828 l.data = &text;
829 l.next = NULL;
830
831 e_cal_component_set_description_list (comp, &l);
832 }
833
834 g_free (str);
835
836 /* Dates */
837
838 due_tt = icaltime_null_time ();
839
840 date.value = &due_tt;
841 date.tzid = NULL;
842
843 /* Due Date. */
844 if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->due_date))) {
845 comp_editor_page_display_validation_error (page, _("Due date is wrong"), priv->due_date);
846 return FALSE;
847 }
848
849 due_date_set = e_date_edit_get_date (
850 E_DATE_EDIT (priv->due_date),
851 &due_tt.year,
852 &due_tt.month,
853 &due_tt.day);
854 if (due_date_set) {
855 due_tt.is_date = TRUE;
856 date.tzid = NULL;
857 e_cal_component_set_due (comp, &date);
858 } else {
859 e_cal_component_set_due (comp, NULL);
860 }
861
862 /* Start Date. */
863 if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->start_date))) {
864 comp_editor_page_display_validation_error (page, _("Start date is wrong"), priv->start_date);
865 return FALSE;
866 }
867
868 start_tt = icaltime_null_time ();
869 date.value = &start_tt;
870 start_date_set = e_date_edit_get_date (
871 E_DATE_EDIT (priv->start_date),
872 &start_tt.year,
873 &start_tt.month,
874 &start_tt.day);
875 if (start_date_set) {
876 start_tt.is_date = TRUE;
877 date.tzid = NULL;
878 e_cal_component_set_dtstart (comp, &date);
879 } else {
880 e_cal_component_set_dtstart (comp, NULL);
881 }
882
883 /* Classification. */
884 classification = comp_editor_get_classification (editor);
885 e_cal_component_set_classification (comp, classification);
886
887 /* send options */
888 if (priv->sendoptions_shown && priv->sod) {
889 icaltimezone *zone = comp_editor_get_timezone (editor);
890 e_send_options_utils_fill_component (priv->sod, comp, zone);
891 }
892
893 /* Categories */
894 cat = gtk_editable_get_chars (GTK_EDITABLE (priv->categories), 0, -1);
895 str = comp_editor_strip_categories (cat);
896 g_free (cat);
897
898 e_cal_component_set_categories (comp, str);
899
900 if (str)
901 g_free (str);
902
903 if (priv->is_assignment) {
904 ECalComponentOrganizer organizer = {NULL, NULL, NULL, NULL};
905
906 if (!priv->existing) {
907 gchar *backend_addr = NULL;
908 gchar *backend_mailto = NULL;
909 gchar *name;
910 gchar *mailto;
911
912 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
913
914 /* Find the identity for the organizer or sentby field */
915 if (!get_current_identity (tpage, &name, &mailto)) {
916 e_notice (
917 priv->main, GTK_MESSAGE_ERROR,
918 _("An organizer is required."));
919 return FALSE;
920 }
921
922 /* Prefer the backend address if we have one. */
923 if (backend_addr != NULL && *backend_addr != '\0') {
924 backend_mailto = g_strdup_printf (
925 "MAILTO:%s", backend_addr);
926 if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) {
927 g_free (backend_mailto);
928 backend_mailto = NULL;
929 }
930 }
931
932 if (backend_mailto == NULL) {
933 organizer.cn = name;
934 organizer.value = mailto;
935 name = mailto = NULL;
936 } else {
937 organizer.value = backend_mailto;
938 organizer.sentby = mailto;
939 backend_mailto = mailto = NULL;
940 }
941
942 e_cal_component_set_organizer (comp, &organizer);
943
944 g_free (backend_addr);
945 g_free (backend_mailto);
946 g_free (name);
947 g_free (mailto);
948 }
949
950 if (e_meeting_store_count_actual_attendees (priv->meeting_store) < 1) {
951 e_notice (
952 priv->main, GTK_MESSAGE_ERROR,
953 _("At least one attendee is required."));
954 return FALSE;
955 }
956
957 if (flags & COMP_EDITOR_DELEGATE) {
958 GSList *attendee_list, *l;
959 gint i;
960 const GPtrArray *attendees = e_meeting_store_get_attendees (priv->meeting_store);
961
962 e_cal_component_get_attendee_list (priv->comp, &attendee_list);
963
964 for (i = 0; i < attendees->len; i++) {
965 EMeetingAttendee *ia = g_ptr_array_index (attendees, i);
966 ECalComponentAttendee *ca;
967
968 /* Remove the duplicate user from the component if present */
969 if (e_meeting_attendee_is_set_delfrom (ia) || e_meeting_attendee_is_set_delto (ia)) {
970 for (l = attendee_list; l; l = l->next) {
971 ECalComponentAttendee *a = l->data;
972
973 if (g_str_equal (a->value, e_meeting_attendee_get_address (ia))) {
974 attendee_list = g_slist_remove (attendee_list, l->data);
975 break;
976 }
977 }
978 }
979
980 ca = e_meeting_attendee_as_e_cal_component_attendee (ia);
981
982 attendee_list = g_slist_append (attendee_list, ca);
983 }
984 e_cal_component_set_attendee_list (comp, attendee_list);
985 e_cal_component_free_attendee_list (attendee_list);
986 } else
987 set_attendees (comp, e_meeting_store_get_attendees (priv->meeting_store));
988 }
989
990 return TRUE;
991 }
992
993 static gboolean
994 task_page_fill_timezones (CompEditorPage *page,
995 GHashTable *timezones)
996 {
997 TaskPage *tpage;
998 TaskPagePrivate *priv;
999 icaltimezone *zone;
1000
1001 tpage = TASK_PAGE (page);
1002 priv = tpage->priv;
1003
1004 /* add start date timezone */
1005 zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->timezone));
1006 if (zone) {
1007 if (!g_hash_table_lookup (timezones, icaltimezone_get_tzid (zone)))
1008 g_hash_table_insert (timezones, (gpointer) icaltimezone_get_tzid (zone), zone);
1009 }
1010
1011 return TRUE;
1012 }
1013
1014 static void
1015 task_page_add_attendee (CompEditorPage *page,
1016 EMeetingAttendee *attendee)
1017 {
1018 CompEditor *editor;
1019 TaskPagePrivate *priv;
1020
1021 priv = TASK_PAGE_GET_PRIVATE (page);
1022 editor = comp_editor_page_get_editor (page);
1023
1024 if ((comp_editor_get_flags (editor) & COMP_EDITOR_DELEGATE) != 0) {
1025 gchar *delfrom;
1026
1027 /* EMeetingAttendee takes ownership of the string. */
1028 delfrom = g_strdup_printf ("MAILTO:%s", priv->user_add);
1029 e_meeting_attendee_set_delfrom (attendee, delfrom);
1030 }
1031
1032 e_meeting_store_add_attendee (priv->meeting_store, attendee);
1033 e_meeting_list_view_add_attendee_to_name_selector (
1034 E_MEETING_LIST_VIEW (priv->list_view), attendee);
1035 }
1036
1037 static void
1038 task_page_class_init (TaskPageClass *class)
1039 {
1040 GObjectClass *object_class;
1041 CompEditorPageClass *editor_page_class;
1042
1043 g_type_class_add_private (class, sizeof (TaskPagePrivate));
1044
1045 object_class = G_OBJECT_CLASS (class);
1046 object_class->dispose = task_page_dispose;
1047 object_class->finalize = task_page_finalize;
1048
1049 editor_page_class = COMP_EDITOR_PAGE_CLASS (class);
1050 editor_page_class->get_widget = task_page_get_widget;
1051 editor_page_class->focus_main_widget = task_page_focus_main_widget;
1052 editor_page_class->fill_widgets = task_page_fill_widgets;
1053 editor_page_class->fill_component = task_page_fill_component;
1054 editor_page_class->fill_timezones = task_page_fill_timezones;
1055 editor_page_class->add_attendee = task_page_add_attendee;
1056 }
1057
1058 static void
1059 task_page_init (TaskPage *tpage)
1060 {
1061 tpage->priv = TASK_PAGE_GET_PRIVATE (tpage);
1062 tpage->priv->deleted_attendees = g_ptr_array_new ();
1063 tpage->priv->open_cancellable = NULL;
1064 }
1065
1066 void
1067 task_page_set_view_role (TaskPage *page,
1068 gboolean state)
1069 {
1070 TaskPagePrivate *priv = page->priv;
1071
1072 e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_ROLE_COL, state);
1073 }
1074
1075 void
1076 task_page_set_view_status (TaskPage *page,
1077 gboolean state)
1078 {
1079 TaskPagePrivate *priv = page->priv;
1080
1081 e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_STATUS_COL, state);
1082 }
1083
1084 void
1085 task_page_set_view_type (TaskPage *page,
1086 gboolean state)
1087 {
1088 TaskPagePrivate *priv = page->priv;
1089
1090 e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_TYPE_COL, state);
1091 }
1092
1093 void
1094 task_page_set_view_rsvp (TaskPage *page,
1095 gboolean state)
1096 {
1097 TaskPagePrivate *priv = page->priv;
1098
1099 e_meeting_list_view_column_set_visible (priv->list_view, E_MEETING_STORE_RSVP_COL, state);
1100 }
1101
1102 void
1103 task_page_hide_options (TaskPage *page)
1104 {
1105 CompEditor *editor;
1106 GtkAction *action;
1107
1108 g_return_if_fail (IS_TASK_PAGE (page));
1109
1110 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1111 action = comp_editor_get_action (editor, "send-options");
1112 gtk_action_set_visible (action, FALSE);
1113 }
1114
1115 void
1116 task_page_show_options (TaskPage *page)
1117 {
1118 CompEditor *editor;
1119 GtkAction *action;
1120
1121 g_return_if_fail (IS_TASK_PAGE (page));
1122
1123 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1124 action = comp_editor_get_action (editor, "send-options");
1125 gtk_action_set_visible (action, TRUE);
1126 }
1127
1128 void
1129 task_page_set_assignment (TaskPage *page,
1130 gboolean set)
1131 {
1132 g_return_if_fail (IS_TASK_PAGE (page));
1133
1134 page->priv->is_assignment = set;
1135 sensitize_widgets (page);
1136 }
1137
1138 static void
1139 add_clicked_cb (GtkButton *btn,
1140 TaskPage *page)
1141 {
1142 EMeetingAttendee *attendee;
1143 CompEditor *editor;
1144 CompEditorFlags flags;
1145
1146 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1147 flags = comp_editor_get_flags (editor);
1148
1149 attendee = e_meeting_store_add_attendee_with_defaults (page->priv->meeting_store);
1150
1151 if (flags & COMP_EDITOR_DELEGATE) {
1152 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", page->priv->user_add));
1153 }
1154
1155 e_meeting_list_view_edit (page->priv->list_view, attendee);
1156 }
1157
1158 static void edit_clicked_cb (GtkButton *btn, TaskPage *tpage)
1159 {
1160 TaskPagePrivate *priv;
1161 GtkTreePath *path = NULL;
1162 GtkTreeViewColumn *focus_col;
1163
1164 priv = tpage->priv;
1165
1166 gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->list_view), &path, NULL);
1167 g_return_if_fail (path != NULL);
1168
1169 gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->list_view), &path, &focus_col);
1170 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->list_view), path, focus_col, TRUE);
1171 gtk_tree_path_free (path);
1172 }
1173
1174 static gboolean
1175 existing_attendee (EMeetingAttendee *ia,
1176 ECalComponent *comp)
1177 {
1178 GSList *attendees, *l;
1179 const gchar *ia_address;
1180 const gchar *ia_sentby = NULL;
1181
1182 ia_address = itip_strip_mailto (e_meeting_attendee_get_address (ia));
1183 if (!ia_address)
1184 return FALSE;
1185
1186 if (e_meeting_attendee_is_set_sentby (ia))
1187 ia_sentby = itip_strip_mailto (e_meeting_attendee_get_sentby (ia));
1188
1189 e_cal_component_get_attendee_list (comp, &attendees);
1190
1191 for (l = attendees; l; l = l->next) {
1192 ECalComponentAttendee *attendee = l->data;
1193 const gchar *address;
1194 const gchar *sentby = NULL;
1195
1196 address = itip_strip_mailto (attendee->value);
1197 if (attendee->sentby)
1198 sentby = itip_strip_mailto (attendee->sentby);
1199
1200 if ((address && !g_ascii_strcasecmp (ia_address, address)) || (sentby && ia_sentby && !g_ascii_strcasecmp (ia_sentby, sentby))) {
1201 e_cal_component_free_attendee_list (attendees);
1202 return TRUE;
1203 }
1204 }
1205
1206 e_cal_component_free_attendee_list (attendees);
1207
1208 return FALSE;
1209 }
1210
1211 static void
1212 remove_attendee (TaskPage *page,
1213 EMeetingAttendee *ia)
1214 {
1215 TaskPagePrivate *priv = page->priv;
1216 CompEditor *editor;
1217 CompEditorFlags flags;
1218 gint pos = 0;
1219
1220 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1221 flags = comp_editor_get_flags (editor);
1222
1223 /* If the user deletes the organizer attendee explicitly,
1224 * assume they no longer want the organizer showing up */
1225 if (ia == priv->ia) {
1226 g_object_unref (priv->ia);
1227 priv->ia = NULL;
1228 }
1229
1230 /* If this was a delegatee, no longer delegate */
1231 if (e_meeting_attendee_is_set_delfrom (ia)) {
1232 EMeetingAttendee *ib;
1233
1234 ib = e_meeting_store_find_attendee (priv->meeting_store, e_meeting_attendee_get_delfrom (ia), &pos);
1235 if (ib != NULL) {
1236 e_meeting_attendee_set_delto (ib, NULL);
1237
1238 if (!(flags & COMP_EDITOR_DELEGATE))
1239 e_meeting_attendee_set_edit_level (ib, E_MEETING_ATTENDEE_EDIT_FULL);
1240 }
1241 }
1242
1243 /* Handle deleting all attendees in the delegation chain */
1244 while (ia != NULL) {
1245 EMeetingAttendee *ib = NULL;
1246
1247 if (existing_attendee (ia, priv->comp) && !comp_editor_have_in_new_attendees (priv->comp, ia)) {
1248 g_object_ref (ia);
1249 g_ptr_array_add (priv->deleted_attendees, ia);
1250 }
1251
1252 if (e_meeting_attendee_get_delto (ia) != NULL)
1253 ib = e_meeting_store_find_attendee (priv->meeting_store, e_meeting_attendee_get_delto (ia), NULL);
1254
1255 comp_editor_manage_new_attendees (priv->comp, ia, FALSE);
1256 e_meeting_list_view_remove_attendee_from_name_selector (priv->list_view, ia);
1257 e_meeting_store_remove_attendee (priv->meeting_store, ia);
1258
1259 ia = ib;
1260 }
1261
1262 sensitize_widgets (page);
1263 }
1264
1265 static void
1266 remove_clicked_cb (GtkButton *btn,
1267 TaskPage *page)
1268 {
1269 TaskPagePrivate *priv;
1270 EMeetingAttendee *ia;
1271 GtkTreeSelection *selection;
1272 GList *paths = NULL, *tmp;
1273 GtkTreeIter iter;
1274 GtkTreePath *path = NULL;
1275 GtkTreeModel *model = NULL;
1276 gboolean valid_iter;
1277 gchar *address;
1278
1279 priv = page->priv;
1280
1281 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
1282 model = GTK_TREE_MODEL (priv->meeting_store);
1283 if (!(paths = gtk_tree_selection_get_selected_rows (selection, &model))) {
1284 g_warning ("Could not get a selection to delete.");
1285 return;
1286 }
1287 paths = g_list_reverse (paths);
1288
1289 for (tmp = paths; tmp; tmp = tmp->next) {
1290 path = tmp->data;
1291
1292 gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path);
1293
1294 gtk_tree_model_get (GTK_TREE_MODEL (priv->meeting_store), &iter, E_MEETING_STORE_ADDRESS_COL, &address, -1);
1295 ia = e_meeting_store_find_attendee (priv->meeting_store, address, NULL);
1296 g_free (address);
1297 if (!ia) {
1298 g_warning ("Cannot delete attendee\n");
1299 continue;
1300 } else if (e_meeting_attendee_get_edit_level (ia) != E_MEETING_ATTENDEE_EDIT_FULL) {
1301 g_warning ("Not enough rights to delete attendee: %s\n", e_meeting_attendee_get_address (ia));
1302 continue;
1303 }
1304
1305 remove_attendee (page, ia);
1306 }
1307
1308 /* Select closest item after removal */
1309 valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path);
1310 if (!valid_iter) {
1311 gtk_tree_path_prev (path);
1312 valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->meeting_store), &iter, path);
1313 }
1314
1315 if (valid_iter) {
1316 gtk_tree_selection_unselect_all (selection);
1317 gtk_tree_selection_select_iter (selection, &iter);
1318 }
1319
1320 g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL);
1321 g_list_free (paths);
1322 }
1323
1324 static void
1325 invite_cb (GtkWidget *widget,
1326 TaskPage *page)
1327 {
1328 e_meeting_list_view_invite_others_dialog (page->priv->list_view);
1329 }
1330
1331 static void
1332 attendee_added_cb (EMeetingListView *emlv,
1333 EMeetingAttendee *ia,
1334 TaskPage *page)
1335 {
1336 TaskPagePrivate *priv = page->priv;
1337 CompEditor *editor;
1338 CompEditorFlags flags;
1339 ECalClient *client;
1340
1341 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1342 client = comp_editor_get_client (editor);
1343 flags = comp_editor_get_flags (editor);
1344
1345 if (!(flags & COMP_EDITOR_DELEGATE)) {
1346 comp_editor_manage_new_attendees (priv->comp, ia, TRUE);
1347 return;
1348 }
1349
1350 /* do not remove here, it did EMeetingListView already */
1351 e_meeting_attendee_set_delfrom (ia, g_strdup_printf ("MAILTO:%s", priv->user_add ? priv->user_add : ""));
1352
1353 if (!e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY)) {
1354 EMeetingAttendee *delegator;
1355
1356 gtk_widget_set_sensitive (priv->invite, FALSE);
1357 gtk_widget_set_sensitive (priv->add, FALSE);
1358 gtk_widget_set_sensitive (priv->edit, FALSE);
1359
1360 delegator = e_meeting_store_find_attendee (priv->meeting_store, priv->user_add, NULL);
1361 g_return_if_fail (delegator != NULL);
1362
1363 e_meeting_attendee_set_delto (delegator, g_strdup (e_meeting_attendee_get_address (ia)));
1364 }
1365 }
1366
1367 static gboolean
1368 list_view_event (EMeetingListView *list_view,
1369 GdkEvent *event,
1370 TaskPage *page)
1371 {
1372 TaskPagePrivate *priv= page->priv;
1373 CompEditor *editor;
1374 CompEditorFlags flags;
1375
1376 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
1377 flags = comp_editor_get_flags (editor);
1378
1379 if (event->type == GDK_2BUTTON_PRESS && flags & COMP_EDITOR_USER_ORG) {
1380 EMeetingAttendee *attendee;
1381
1382 attendee = e_meeting_store_add_attendee_with_defaults (priv->meeting_store);
1383
1384 if (flags & COMP_EDITOR_DELEGATE) {
1385 e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", page->priv->user_add));
1386 }
1387
1388 e_meeting_list_view_edit (page->priv->list_view, attendee);
1389 return TRUE;
1390 }
1391
1392 return FALSE;
1393 }
1394
1395 static gboolean
1396 list_key_press (EMeetingListView *list_view,
1397 GdkEventKey *event,
1398 TaskPage *page)
1399 {
1400 if (event->keyval == GDK_KEY_Delete) {
1401
1402 remove_clicked_cb (NULL, page);
1403
1404 return TRUE;
1405 } else if (event->keyval == GDK_KEY_Insert) {
1406 add_clicked_cb (NULL, page);
1407
1408 return TRUE;
1409 }
1410
1411 return FALSE;
1412 }
1413
1414 void
1415 task_page_set_show_timezone (TaskPage *page,
1416 gboolean state)
1417 {
1418 if (state) {
1419 gtk_widget_show_all (page->priv->timezone);
1420 gtk_widget_show (page->priv->timezone_label);
1421 } else {
1422 gtk_widget_hide (page->priv->timezone);
1423 gtk_widget_hide (page->priv->timezone_label);
1424 }
1425
1426 }
1427
1428 void
1429 task_page_set_show_categories (TaskPage *page,
1430 gboolean state)
1431 {
1432 if (state) {
1433 gtk_widget_show (page->priv->categories_btn);
1434 gtk_widget_show (page->priv->categories);
1435 } else {
1436 gtk_widget_hide (page->priv->categories_btn);
1437 gtk_widget_hide (page->priv->categories);
1438 }
1439 }
1440
1441 /*If the msg has some value set, the icon should always be set */
1442 void
1443 task_page_set_info_string (TaskPage *tpage,
1444 const gchar *icon,
1445 const gchar *msg)
1446 {
1447 TaskPagePrivate *priv;
1448
1449 priv = tpage->priv;
1450
1451 gtk_image_set_from_stock (GTK_IMAGE (priv->info_icon), icon, GTK_ICON_SIZE_BUTTON);
1452 gtk_label_set_markup (GTK_LABEL (priv->info_string), msg);
1453
1454 if (msg && icon)
1455 gtk_widget_show (priv->info_hbox);
1456 else
1457 gtk_widget_hide (priv->info_hbox);
1458 }
1459
1460 /* Gets the widgets from the XML file and returns if they are all available. */
1461 static gboolean
1462 get_widgets (TaskPage *tpage)
1463 {
1464 EShell *shell;
1465 ESourceRegistry *registry;
1466 CompEditor *editor;
1467 CompEditorPage *page = COMP_EDITOR_PAGE (tpage);
1468 GtkEntryCompletion *completion;
1469 TaskPagePrivate *priv;
1470 GSList *accel_groups;
1471 GtkWidget *toplevel;
1472 GtkWidget *parent;
1473 GtkWidget *sw;
1474 GtkTreeSelection *selection;
1475
1476 priv = tpage->priv;
1477
1478 editor = comp_editor_page_get_editor (page);
1479 shell = comp_editor_get_shell (editor);
1480 registry = e_shell_get_registry (shell);
1481
1482 priv->main = e_builder_get_widget (priv->builder, "task-page");
1483 if (!priv->main)
1484 return FALSE;
1485
1486 /* Get the GtkAccelGroup from the toplevel window, so we can install
1487 * it when the notebook page is mapped. */
1488 toplevel = gtk_widget_get_toplevel (priv->main);
1489 accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel));
1490 if (accel_groups)
1491 page->accel_group = g_object_ref (accel_groups->data);
1492
1493 g_object_ref (priv->main);
1494 parent = gtk_widget_get_parent (priv->main);
1495 gtk_container_remove (GTK_CONTAINER (parent), priv->main);
1496
1497 priv->info_hbox = e_builder_get_widget (priv->builder, "generic-info");
1498 priv->info_icon = e_builder_get_widget (priv->builder, "generic-info-image");
1499 priv->info_string = e_builder_get_widget (priv->builder, "generic-info-msgs");
1500
1501 priv->summary = e_builder_get_widget (priv->builder, "summary");
1502 priv->summary_label = e_builder_get_widget (priv->builder, "summary-label");
1503
1504 /* Glade's visibility flag doesn't seem to work for custom widgets */
1505 priv->due_date = e_builder_get_widget (priv->builder, "due-date");
1506 gtk_widget_show (priv->due_date);
1507 priv->start_date = e_builder_get_widget (priv->builder, "start-date");
1508 gtk_widget_show (priv->start_date);
1509
1510 priv->timezone = e_builder_get_widget (priv->builder, "timezone");
1511 priv->timezone_label = e_builder_get_widget (priv->builder, "timezone-label");
1512 priv->attendees_label = e_builder_get_widget (priv->builder, "attendees-label");
1513 priv->description = e_builder_get_widget (priv->builder, "description");
1514 priv->categories_btn = e_builder_get_widget (priv->builder, "categories-button");
1515 priv->categories = e_builder_get_widget (priv->builder, "categories");
1516
1517 priv->organizer = e_builder_get_widget (priv->builder, "organizer");
1518 gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->organizer))));
1519 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->organizer), 0);
1520
1521 priv->invite = e_builder_get_widget (priv->builder, "invite");
1522 priv->add = e_builder_get_widget (priv->builder, "add-attendee");
1523 priv->edit = e_builder_get_widget (priv->builder, "edit-attendee");
1524 priv->remove = e_builder_get_widget (priv->builder, "remove-attendee");
1525 priv->list_box = e_builder_get_widget (priv->builder, "list-box");
1526 priv->calendar_label = e_builder_get_widget (priv->builder, "group-label");
1527 priv->attendee_box = e_builder_get_widget (priv->builder, "attendee-box");
1528 priv->org_cal_label = e_builder_get_widget (priv->builder, "org-task-label");
1529
1530 priv->list_view = e_meeting_list_view_new (priv->meeting_store);
1531
1532 selection = gtk_tree_view_get_selection ((GtkTreeView *) priv->list_view);
1533 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1534 gtk_widget_show (GTK_WIDGET (priv->list_view));
1535
1536 sw = gtk_scrolled_window_new (NULL, NULL);
1537 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1538 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
1539 gtk_widget_show (sw);
1540 gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (priv->list_view));
1541 gtk_box_pack_start (GTK_BOX (priv->list_box), sw, TRUE, TRUE, 0);
1542
1543 priv->source_combo_box = e_builder_get_widget (priv->builder, "source");
1544 e_source_combo_box_set_registry (
1545 E_SOURCE_COMBO_BOX (priv->source_combo_box), registry);
1546
1547 gtk_label_set_mnemonic_widget (GTK_LABEL (priv->calendar_label), priv->source_combo_box);
1548
1549 completion = e_category_completion_new ();
1550 gtk_entry_set_completion (GTK_ENTRY (priv->categories), completion);
1551 g_object_unref (completion);
1552
1553 if (priv->summary) {
1554 EShell *shell;
1555 EShellSettings *shell_settings;
1556 CompEditor *editor;
1557
1558 editor = comp_editor_page_get_editor (page);
1559 shell = comp_editor_get_shell (editor);
1560 shell_settings = e_shell_get_shell_settings (shell);
1561
1562 g_object_bind_property (
1563 shell_settings, "composer-inline-spelling",
1564 priv->summary, "checking-enabled",
1565 G_BINDING_SYNC_CREATE);
1566 }
1567
1568 return (priv->summary
1569 && priv->summary_label
1570 && priv->due_date
1571 && priv->start_date
1572 && priv->timezone
1573 && priv->description
1574 && priv->categories_btn
1575 && priv->categories
1576 && priv->organizer
1577 );
1578 }
1579
1580 static void
1581 summary_changed_cb (GtkEntry *entry,
1582 CompEditorPage *page)
1583 {
1584 CompEditor *editor;
1585 const gchar *text;
1586
1587 if (comp_editor_page_get_updating (page))
1588 return;
1589
1590 editor = comp_editor_page_get_editor (page);
1591 text = gtk_entry_get_text (entry);
1592 comp_editor_set_summary (editor, text);
1593 }
1594
1595 /* Callback used when the start or due date widgets change. We notify the
1596 * other pages in the task editor, so they can update any labels.
1597 */
1598 static void
1599 date_changed_cb (EDateEdit *dedit,
1600 TaskPage *tpage)
1601 {
1602 TaskPagePrivate *priv = tpage->priv;
1603 CompEditorPageDates dates;
1604 gboolean date_set;
1605 ECalComponentDateTime start_dt, due_dt;
1606 struct icaltimetype start_tt = icaltime_null_time ();
1607 struct icaltimetype due_tt = icaltime_null_time ();
1608
1609 if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tpage)))
1610 return;
1611
1612 date_set = e_date_edit_get_date (
1613 E_DATE_EDIT (priv->start_date),
1614 &start_tt.year,
1615 &start_tt.month,
1616 &start_tt.day);
1617 if (date_set) {
1618 start_tt.is_date = TRUE;
1619 start_dt.tzid = NULL;
1620 } else {
1621 start_tt = icaltime_null_time ();
1622 start_dt.tzid = NULL;
1623 }
1624
1625 date_set = e_date_edit_get_date (
1626 E_DATE_EDIT (priv->due_date),
1627 &due_tt.year,
1628 &due_tt.month,
1629 &due_tt.day);
1630 if (date_set) {
1631 due_tt.is_date = TRUE;
1632 due_dt.tzid = NULL;
1633 } else {
1634 due_tt = icaltime_null_time ();
1635 due_dt.tzid = NULL;
1636 }
1637
1638 start_dt.value = &start_tt;
1639 dates.start = &start_dt;
1640 dates.end = NULL;
1641 due_dt.value = &due_tt;
1642 dates.due = &due_dt;
1643 dates.complete = NULL;
1644
1645 /* Notify upstream */
1646 comp_editor_page_notify_dates_changed (COMP_EDITOR_PAGE (tpage),
1647 &dates);
1648
1649 check_starts_in_the_past (tpage);
1650 }
1651
1652 static void
1653 timezone_changed_cb (EDateEdit *dedit,
1654 TaskPage *tpage)
1655 {
1656 date_changed_cb ((EDateEdit *) tpage->priv->start_date, tpage);
1657 date_changed_cb ((EDateEdit *) tpage->priv->due_date, tpage);
1658 }
1659
1660 /* Callback used when the categories button is clicked; we must bring up the
1661 * category list dialog.
1662 */
1663 static void
1664 categories_clicked_cb (GtkWidget *button,
1665 TaskPage *tpage)
1666 {
1667 GtkEntry *entry;
1668
1669 entry = GTK_ENTRY (tpage->priv->categories);
1670 e_categories_config_open_dialog_for_entry (entry);
1671 }
1672
1673 static gboolean
1674 check_start_before_end (struct icaltimetype *start_tt,
1675 icaltimezone *start_zone,
1676 struct icaltimetype *end_tt,
1677 icaltimezone *end_zone,
1678 gboolean adjust_end_time,
1679 gboolean adjust_by_hour)
1680 {
1681 struct icaltimetype end_tt_copy;
1682 gint cmp;
1683
1684 /* Convert the end time to the same timezone as the start time. */
1685 end_tt_copy = *end_tt;
1686 icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone);
1687
1688 /* Now check if the start time is after the end time. If it is,
1689 * we need to modify one of the times. */
1690 cmp = icaltime_compare (*start_tt, end_tt_copy);
1691 if (cmp > 0) {
1692 if (adjust_end_time) {
1693 /* Modify the end time, to be the start + 1 hour/day. */
1694 *end_tt = *start_tt;
1695 icaltime_adjust (end_tt, 0, adjust_by_hour ? 1 : 24, 0, 0);
1696 icaltimezone_convert_time (
1697 end_tt, start_zone,
1698 end_zone);
1699 } else {
1700 /* Modify the start time, to be the end - 1 hour/day. */
1701 *start_tt = *end_tt;
1702 icaltime_adjust (start_tt, 0, adjust_by_hour ? -1 : -24, 0, 0);
1703 icaltimezone_convert_time (
1704 start_tt, end_zone,
1705 start_zone);
1706 }
1707 return TRUE;
1708 }
1709
1710 return FALSE;
1711 }
1712
1713 /*
1714 * This is called whenever the start or due dates.
1715 * It makes sure that the start date < end date. It also emits the notification
1716 * signals so the other event editor pages update their labels etc.
1717 *
1718 * If adjust_end_time is TRUE, if the start time < end time it will adjust
1719 * the end time. If FALSE it will adjust the start time. If the user sets the
1720 * start or end time, the other time is adjusted to make it valid.
1721 *
1722 * Time part of the value is changed only when both edits have time set,
1723 * otherwise times will differ one hour.
1724 */
1725 static void
1726 times_updated (TaskPage *tpage,
1727 gboolean adjust_end_time)
1728 {
1729 TaskPagePrivate *priv;
1730 struct icaltimetype start_tt = icaltime_null_time ();
1731 struct icaltimetype end_tt = icaltime_null_time ();
1732 gboolean date_set;
1733 gboolean set_start_date = FALSE, set_end_date = FALSE;
1734 icaltimezone *zone;
1735
1736 priv = tpage->priv;
1737
1738 if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tpage)))
1739 return;
1740
1741 date_set = e_date_edit_get_date (
1742 E_DATE_EDIT (priv->start_date),
1743 &start_tt.year,
1744 &start_tt.month,
1745 &start_tt.day);
1746 if (!date_set)
1747 return;
1748
1749 date_set = e_date_edit_get_date (
1750 E_DATE_EDIT (priv->due_date),
1751 &end_tt.year,
1752 &end_tt.month,
1753 &end_tt.day);
1754 if (!date_set)
1755 return;
1756
1757 zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY (priv->timezone));
1758
1759 if (check_start_before_end (&start_tt, zone,
1760 &end_tt, zone,
1761 adjust_end_time,
1762 FALSE)) {
1763 if (adjust_end_time)
1764 set_end_date = TRUE;
1765 else
1766 set_start_date = TRUE;
1767 }
1768
1769 if (set_start_date) {
1770 g_signal_handlers_block_matched (priv->start_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1771 e_date_edit_set_date (E_DATE_EDIT (priv->start_date), start_tt.year, start_tt.month, start_tt.day);
1772 g_signal_handlers_unblock_matched (priv->start_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1773 }
1774
1775 if (set_end_date) {
1776 g_signal_handlers_block_matched (priv->due_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1777 e_date_edit_set_date (E_DATE_EDIT (priv->due_date), end_tt.year, end_tt.month, end_tt.day);
1778 g_signal_handlers_unblock_matched (priv->due_date, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, tpage);
1779 }
1780
1781 /* Notify upstream */
1782 date_changed_cb ((EDateEdit *) priv->start_date, tpage);
1783 date_changed_cb ((EDateEdit *) priv->due_date, tpage);
1784 }
1785
1786 static void
1787 start_date_changed_cb (TaskPage *tpage)
1788 {
1789 times_updated (tpage, TRUE);
1790 }
1791
1792 static void
1793 due_date_changed_cb (TaskPage *tpage)
1794 {
1795 times_updated (tpage, FALSE);
1796 }
1797
1798 static void
1799 tpage_client_opened_cb (GObject *source_object,
1800 GAsyncResult *result,
1801 gpointer user_data)
1802 {
1803 ESource *source = E_SOURCE (source_object);
1804 EClient *client = NULL;
1805 TaskPage *tpage = user_data;
1806 TaskPagePrivate *priv;
1807 CompEditor *editor;
1808 GError *error = NULL;
1809
1810 if (!e_client_utils_open_new_finish (source, result, &client, &error)) {
1811 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
1812 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1813 g_clear_error (&error);
1814 return;
1815 }
1816 }
1817
1818 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
1819 priv = tpage->priv;
1820 if (error) {
1821 GtkWidget *dialog;
1822 ECalClient *old_client;
1823
1824 old_client = comp_editor_get_client (editor);
1825
1826 e_source_combo_box_set_active (
1827 E_SOURCE_COMBO_BOX (priv->source_combo_box),
1828 e_client_get_source (E_CLIENT (old_client)));
1829
1830 dialog = gtk_message_dialog_new (
1831 NULL, GTK_DIALOG_MODAL,
1832 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
1833 _("Unable to open tasks in '%s': %s"),
1834 e_source_get_display_name (source),
1835 error->message);
1836 gtk_dialog_run (GTK_DIALOG (dialog));
1837 gtk_widget_destroy (dialog);
1838
1839 g_clear_error (&error);
1840 } else {
1841 icaltimezone *zone;
1842 ECalClient *cal_client = E_CAL_CLIENT (client);
1843
1844 g_return_if_fail (cal_client != NULL);
1845
1846 zone = comp_editor_get_timezone (editor);
1847 e_cal_client_set_default_timezone (cal_client, zone);
1848
1849 comp_editor_set_client (editor, cal_client);
1850 comp_editor_page_changed (COMP_EDITOR_PAGE (tpage));
1851 if (e_client_check_capability (client, CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS) && priv->is_assignment)
1852 task_page_show_options (tpage);
1853 else
1854 task_page_hide_options (tpage);
1855
1856 if (client) {
1857 gchar *backend_addr = NULL;
1858
1859 e_client_get_backend_property_sync (client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
1860
1861 if (priv->is_assignment)
1862 task_page_select_organizer (tpage, backend_addr);
1863
1864 set_subscriber_info_string (tpage, backend_addr);
1865 g_free (backend_addr);
1866 }
1867
1868 sensitize_widgets (tpage);
1869 }
1870 }
1871
1872 static void
1873 source_changed_cb (ESourceComboBox *source_combo_box,
1874 TaskPage *tpage)
1875 {
1876 TaskPagePrivate *priv = tpage->priv;
1877 ESource *source;
1878
1879 if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (tpage)))
1880 return;
1881
1882 source = e_source_combo_box_ref_active (source_combo_box);
1883 g_return_if_fail (source != NULL);
1884
1885 if (priv->open_cancellable) {
1886 g_cancellable_cancel (priv->open_cancellable);
1887 g_object_unref (priv->open_cancellable);
1888 }
1889 priv->open_cancellable = g_cancellable_new ();
1890
1891 e_client_utils_open_new (
1892 source, E_CLIENT_SOURCE_TYPE_TASKS,
1893 FALSE, priv->open_cancellable,
1894 tpage_client_opened_cb, tpage);
1895
1896 g_object_unref (source);
1897 }
1898
1899 static void
1900 set_subscriber_info_string (TaskPage *tpage,
1901 const gchar *backend_address)
1902 {
1903 if (!check_starts_in_the_past (tpage))
1904 task_page_set_info_string (tpage, NULL, NULL);
1905 }
1906
1907 void
1908 task_page_send_options_clicked_cb (TaskPage *tpage)
1909 {
1910 TaskPagePrivate *priv = tpage->priv;
1911 CompEditor *editor;
1912 GtkWidget *toplevel;
1913 ESource *source;
1914 ECalClient *client;
1915
1916 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
1917 client = comp_editor_get_client (editor);
1918
1919 if (!priv->sod) {
1920 priv->sod = e_send_options_dialog_new ();
1921 priv->sod->data->initialized = TRUE;
1922 source = e_source_combo_box_ref_active (
1923 E_SOURCE_COMBO_BOX (priv->source_combo_box));
1924 e_send_options_utils_set_default_data (
1925 priv->sod, source, "task");
1926 g_object_unref (source);
1927 }
1928
1929 if (e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_GEN_OPTIONS)) {
1930 e_send_options_set_need_general_options (priv->sod, FALSE);
1931 }
1932
1933 toplevel = gtk_widget_get_toplevel (priv->main);
1934 e_send_options_dialog_run (priv->sod, toplevel, E_ITEM_TASK);
1935 }
1936
1937 /* Hooks the widget signals */
1938 static gboolean
1939 init_widgets (TaskPage *tpage)
1940 {
1941 CompEditor *editor;
1942 TaskPagePrivate *priv;
1943 GtkAction *action;
1944 GtkTextBuffer *text_buffer;
1945 icaltimezone *zone;
1946 gboolean active;
1947
1948 priv = tpage->priv;
1949
1950 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
1951
1952 /* Make sure the EDateEdit widgets use our timezones to get the
1953 * current time. */
1954 e_date_edit_set_get_time_callback (
1955 E_DATE_EDIT (priv->start_date),
1956 (EDateEditGetTimeCallback) comp_editor_get_current_time,
1957 g_object_ref (editor),
1958 (GDestroyNotify) g_object_unref);
1959 e_date_edit_set_get_time_callback (
1960 E_DATE_EDIT (priv->due_date),
1961 (EDateEditGetTimeCallback) comp_editor_get_current_time,
1962 g_object_ref (editor),
1963 (GDestroyNotify) g_object_unref);
1964
1965 /* Generic informative messages */
1966 gtk_widget_hide (priv->info_hbox);
1967
1968 /* Summary */
1969 g_signal_connect (
1970 priv->summary, "changed",
1971 G_CALLBACK (summary_changed_cb), tpage);
1972
1973 /* Description */
1974 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->description));
1975
1976 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->description), GTK_WRAP_WORD);
1977
1978 e_buffer_tagger_connect (GTK_TEXT_VIEW (priv->description));
1979
1980 /* Dates */
1981 g_signal_connect (
1982 priv->start_date, "changed",
1983 G_CALLBACK (date_changed_cb), tpage);
1984 g_signal_connect (
1985 priv->due_date, "changed",
1986 G_CALLBACK (date_changed_cb), tpage);
1987
1988 /* time zone changed */
1989 g_signal_connect (
1990 priv->timezone, "changed",
1991 G_CALLBACK (timezone_changed_cb), tpage);
1992
1993 /* Categories button */
1994 g_signal_connect (
1995 priv->categories_btn, "clicked",
1996 G_CALLBACK (categories_clicked_cb), tpage);
1997
1998 /* Source selector */
1999 g_signal_connect (
2000 priv->source_combo_box, "changed",
2001 G_CALLBACK (source_changed_cb), tpage);
2002
2003 /* Connect the default signal handler to use to make sure the "changed"
2004 * field gets set whenever a field is changed. */
2005
2006 /* Belongs to priv->description */
2007 g_signal_connect_swapped (
2008 text_buffer, "changed",
2009 G_CALLBACK (comp_editor_page_changed), tpage);
2010 g_signal_connect_swapped (
2011 priv->summary, "changed",
2012 G_CALLBACK (comp_editor_page_changed), tpage);
2013 g_signal_connect_swapped (
2014 priv->start_date, "changed",
2015 G_CALLBACK (start_date_changed_cb), tpage);
2016 g_signal_connect_swapped (
2017 priv->start_date, "changed",
2018 G_CALLBACK (comp_editor_page_changed), tpage);
2019 g_signal_connect_swapped (
2020 priv->due_date, "changed",
2021 G_CALLBACK (due_date_changed_cb), tpage);
2022 g_signal_connect_swapped (
2023 priv->due_date, "changed",
2024 G_CALLBACK (comp_editor_page_changed), tpage);
2025 g_signal_connect_swapped (
2026 priv->timezone, "changed",
2027 G_CALLBACK (comp_editor_page_changed), tpage);
2028 g_signal_connect_swapped (
2029 priv->categories, "changed",
2030 G_CALLBACK (comp_editor_page_changed), tpage);
2031
2032 g_signal_connect (
2033 priv->list_view, "event",
2034 G_CALLBACK (list_view_event), tpage);
2035 g_signal_connect (
2036 priv->list_view, "key_press_event",
2037 G_CALLBACK (list_key_press), tpage);
2038
2039 /* Add attendee button */
2040 g_signal_connect (
2041 priv->add, "clicked",
2042 G_CALLBACK (add_clicked_cb), tpage);
2043
2044 /* Edit attendee button */
2045 g_signal_connect (
2046 priv->edit, "clicked",
2047 G_CALLBACK (edit_clicked_cb), tpage);
2048
2049 /* Remove attendee button */
2050 g_signal_connect (
2051 priv->remove, "clicked",
2052 G_CALLBACK (remove_clicked_cb), tpage);
2053
2054 /* Contacts button */
2055 g_signal_connect (
2056 priv->invite, "clicked",
2057 G_CALLBACK (invite_cb), tpage);
2058
2059 /* Meeting List View */
2060 g_signal_connect (
2061 priv->list_view, "attendee_added",
2062 G_CALLBACK (attendee_added_cb), tpage);
2063
2064 /* Set the default timezone, so the timezone entry may be hidden. */
2065 zone = comp_editor_get_timezone (editor);
2066 e_timezone_entry_set_default_timezone (E_TIMEZONE_ENTRY (priv->timezone), zone);
2067
2068 action = comp_editor_get_action (editor, "view-time-zone");
2069 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2070 task_page_set_show_timezone (tpage, active);
2071
2072 e_meeting_list_view_column_set_visible (
2073 priv->list_view, E_MEETING_STORE_ATTENDEE_COL, TRUE);
2074
2075 action = comp_editor_get_action (editor, "view-role");
2076 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2077 e_meeting_list_view_column_set_visible (
2078 priv->list_view, E_MEETING_STORE_ROLE_COL, active);
2079
2080 action = comp_editor_get_action (editor, "view-rsvp");
2081 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2082 e_meeting_list_view_column_set_visible (
2083 priv->list_view, E_MEETING_STORE_RSVP_COL, active);
2084
2085 action = comp_editor_get_action (editor, "view-status");
2086 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2087 e_meeting_list_view_column_set_visible (
2088 priv->list_view, E_MEETING_STORE_STATUS_COL, active);
2089
2090 action = comp_editor_get_action (editor, "view-type");
2091 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2092 e_meeting_list_view_column_set_visible (
2093 priv->list_view, E_MEETING_STORE_TYPE_COL, active);
2094
2095 action = comp_editor_get_action (editor, "view-categories");
2096 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2097 task_page_set_show_categories (tpage, active);
2098
2099 return TRUE;
2100 }
2101
2102 static void
2103 task_page_select_organizer (TaskPage *tpage,
2104 const gchar *backend_address)
2105 {
2106 TaskPagePrivate *priv = tpage->priv;
2107 const gchar *default_address;
2108 gint ii;
2109
2110 /* Treat an empty backend address as NULL. */
2111 if (backend_address == NULL && *backend_address == '\0')
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
2112 backend_address = NULL;
2113
2114 default_address = priv->fallback_address;
2115
2116 if (backend_address != NULL) {
2117 for (ii = 0; priv->address_strings[ii] != NULL; ii++) {
2118 if (g_strrstr (priv->address_strings[ii], backend_address) != NULL) {
2119 default_address = priv->address_strings[ii];
2120 break;
2121 }
2122 }
2123 }
2124
2125 if (default_address != NULL) {
2126 if (!priv->comp || !e_cal_component_has_organizer (priv->comp)) {
2127 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->organizer)));
2128
2129 g_signal_handlers_block_by_func (entry, organizer_changed_cb, tpage);
2130 gtk_entry_set_text (entry, default_address);
2131 g_signal_handlers_unblock_by_func (entry, organizer_changed_cb, tpage);
2132 }
2133 } else
2134 g_warning ("No potential organizers!");
2135 }
2136
2137 /**
2138 * task_page_construct:
2139 * @tpage: An task page.
2140 *
2141 * Constructs an task page by loading its Glade data.
2142 *
2143 * Return value: The same object as @tpage, or NULL if the widgets could not be
2144 * created.
2145 **/
2146 TaskPage *
2147 task_page_construct (TaskPage *tpage,
2148 EMeetingStore *meeting_store,
2149 ECalClient *client)
2150 {
2151 EShell *shell;
2152 CompEditor *editor;
2153 ESourceRegistry *registry;
2154 TaskPagePrivate *priv;
2155 GtkComboBox *combo_box;
2156 GtkListStore *list_store;
2157 GtkTreeModel *model;
2158 GtkTreeIter iter;
2159 gint ii;
2160
2161 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (tpage));
2162 shell = comp_editor_get_shell (editor);
2163
2164 priv = tpage->priv;
2165 priv->meeting_store = g_object_ref (meeting_store);
2166 priv->client = client;
2167
2168 /* Make sure our custom widget classes are registered with
2169 * GType before we load the GtkBuilder definition file. */
2170 E_TYPE_DATE_EDIT;
2171 E_TYPE_TIMEZONE_ENTRY;
2172 E_TYPE_SOURCE_COMBO_BOX;
2173 E_TYPE_SPELL_ENTRY;
2174
2175 priv->builder = gtk_builder_new ();
2176 e_load_ui_builder_definition (priv->builder, "task-page.ui");
2177
2178 if (!get_widgets (tpage)) {
2179 g_message (
2180 "task_page_construct(): "
2181 "Could not find all widgets in the XML file!");
2182 return NULL;
2183 }
2184
2185 combo_box = GTK_COMBO_BOX (priv->organizer);
2186 model = gtk_combo_box_get_model (combo_box);
2187 list_store = GTK_LIST_STORE (model);
2188
2189 registry = e_shell_get_registry (shell);
2190 priv->address_strings = itip_get_user_identities (registry);
2191 priv->fallback_address = itip_get_fallback_identity (registry);
2192
2193 /* FIXME Could we just use a GtkComboBoxText? */
2194 for (ii = 0; priv->address_strings[ii] != NULL; ii++) {
2195 gtk_list_store_append (list_store, &iter);
2196 gtk_list_store_set (
2197 list_store, &iter,
2198 0, priv->address_strings[ii], -1);
2199 }
2200
2201 gtk_combo_box_set_active (combo_box, 0);
2202
2203 g_signal_connect (
2204 gtk_bin_get_child (GTK_BIN (priv->organizer)), "changed",
2205 G_CALLBACK (organizer_changed_cb), tpage);
2206
2207 if (!init_widgets (tpage)) {
2208 g_message (
2209 "task_page_construct(): "
2210 "Could not initialize the widgets!");
2211 return NULL;
2212 }
2213
2214 return tpage;
2215 }
2216
2217 /**
2218 * task_page_new:
2219 *
2220 * Creates a new task page.
2221 *
2222 * Return value: A newly-created task page, or NULL if the page could
2223 * not be created.
2224 **/
2225 TaskPage *
2226 task_page_new (EMeetingStore *model,
2227 CompEditor *editor)
2228 {
2229 TaskPage *tpage;
2230 ECalClient *client;
2231
2232 tpage = g_object_new (TYPE_TASK_PAGE, "editor", editor, NULL);
2233 client = comp_editor_get_client (editor);
2234 if (!task_page_construct (tpage, model, client)) {
2235 g_object_unref (tpage);
2236 g_return_val_if_reached (NULL);
2237 }
2238
2239 return tpage;
2240 }
2241
2242 ECalComponent *
2243 task_page_get_cancel_comp (TaskPage *page)
2244 {
2245 TaskPagePrivate *priv;
2246
2247 g_return_val_if_fail (page != NULL, NULL);
2248 g_return_val_if_fail (IS_TASK_PAGE (page), NULL);
2249
2250 priv = page->priv;
2251
2252 if (priv->deleted_attendees->len == 0)
2253 return NULL;
2254
2255 set_attendees (priv->comp, priv->deleted_attendees);
2256
2257 return e_cal_component_clone (priv->comp);
2258 }