No issues found
1 /*
2 * Evolution calendar - Main page of the memo 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 * Nathan Owens <pianocomp81@yahoo.com>
24 *
25 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
26 *
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #include <string.h>
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36
37 #include <libedataserverui/libedataserverui.h>
38
39 #include <e-util/e-util.h>
40 #include <e-util/e-categories-config.h>
41 #include <e-util/e-dialog-utils.h>
42 #include <e-util/e-util-private.h>
43
44 #include <misc/e-dateedit.h>
45 #include <misc/e-spell-entry.h>
46 #include <misc/e-buffer-tagger.h>
47
48 #include "../calendar-config.h"
49 #include "comp-editor.h"
50 #include "comp-editor-util.h"
51 #include "e-send-options-utils.h"
52 #include "memo-page.h"
53
54 #define MEMO_PAGE_GET_PRIVATE(obj) \
55 (G_TYPE_INSTANCE_GET_PRIVATE \
56 ((obj), TYPE_MEMO_PAGE, MemoPagePrivate))
57
58 /* Private part of the MemoPage structure */
59 struct _MemoPagePrivate {
60 GtkBuilder *builder;
61
62 /* Widgets from the UI file */
63 GtkWidget *main;
64
65 GtkWidget *memo_content;
66
67 /* Generic informative messages placeholder */
68 GtkWidget *info_hbox;
69 GtkWidget *info_icon;
70 GtkWidget *info_string;
71
72 /* Organizer */
73 GtkWidget *org_label;
74 GtkWidget *org_combo;
75
76 /* To field */
77 GtkWidget *to_button;
78 GtkWidget *to_hbox;
79 GtkWidget *to_entry;
80
81 /* Summary */
82 GtkWidget *summary_label;
83 GtkWidget *summary_entry;
84
85 /* Start date */
86 GtkWidget *start_label;
87 GtkWidget *start_date;
88
89 GtkWidget *categories_btn;
90 GtkWidget *categories;
91
92 GtkWidget *source_combo_box;
93
94 gchar **address_strings;
95 gchar *fallback_address;
96
97 ENameSelector *name_selector;
98
99 GCancellable *open_cancellable;
100 };
101
102 static void set_subscriber_info_string (MemoPage *mpage, const gchar *backend_address);
103 static const gchar * get_recipients (ECalComponent *comp);
104 static void sensitize_widgets (MemoPage *mpage);
105 static gboolean memo_page_fill_component (CompEditorPage *page, ECalComponent *comp);
106 static void memo_page_select_organizer (MemoPage *mpage, const gchar *backend_address);
107
108 G_DEFINE_TYPE (MemoPage, memo_page, TYPE_COMP_EDITOR_PAGE)
109
110 static gboolean
111 get_current_identity (MemoPage *page,
112 gchar **name,
113 gchar **mailto)
114 {
115 EShell *shell;
116 CompEditor *editor;
117 ESourceRegistry *registry;
118 GList *list, *iter;
119 GtkWidget *entry;
120 const gchar *extension_name;
121 const gchar *text;
122 gboolean match = FALSE;
123
124 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (page));
125 shell = comp_editor_get_shell (editor);
126
127 entry = gtk_bin_get_child (GTK_BIN (page->priv->org_combo));
128 text = gtk_entry_get_text (GTK_ENTRY (entry));
129
130 if (text == NULL || *text == '\0')
131 return FALSE;
132
133 registry = e_shell_get_registry (shell);
134 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
135
136 list = e_source_registry_list_sources (registry, extension_name);
137
138 for (iter = list; !match && iter != NULL; iter = g_list_next (iter)) {
139 ESource *source = E_SOURCE (iter->data);
140 ESourceMailIdentity *extension;
141 const gchar *id_name;
142 const gchar *id_address;
143 gchar *identity;
144
145 extension = e_source_get_extension (source, extension_name);
146
147 id_name = e_source_mail_identity_get_name (extension);
148 id_address = e_source_mail_identity_get_address (extension);
149
150 if (id_name == NULL || id_address == NULL)
151 continue;
152
153 identity = g_strdup_printf ("%s <%s>", id_name, id_address);
154 match = (g_ascii_strcasecmp (text, identity) == 0);
155 g_free (identity);
156
157 if (match && name != NULL)
158 *name = g_strdup (id_name);
159
160 if (match && mailto != NULL)
161 *mailto = g_strdup_printf ("MAILTO:%s", id_address);
162 }
163
164 g_list_free_full (list, (GDestroyNotify) g_object_unref);
165
166 return match;
167 }
168
169 /* Fills the widgets with default values */
170 static void
171 clear_widgets (MemoPage *mpage)
172 {
173 GtkTextBuffer *buffer;
174 GtkTextView *view;
175 CompEditor *editor;
176
177 /* Summary */
178 gtk_entry_set_text (GTK_ENTRY (mpage->priv->summary_entry), "");
179
180 /* Description */
181 view = GTK_TEXT_VIEW (mpage->priv->memo_content);
182 buffer = gtk_text_view_get_buffer (view);
183 gtk_text_buffer_set_text (buffer, "", 0);
184 e_buffer_tagger_update_tags (view);
185
186 /* Classification */
187 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage));
188 comp_editor_set_classification (editor, E_CAL_COMPONENT_CLASS_PRIVATE);
189
190 /* Categories */
191 gtk_entry_set_text (GTK_ENTRY (mpage->priv->categories), "");
192 }
193
194 static void
195 memo_page_dispose (GObject *object)
196 {
197 MemoPagePrivate *priv;
198
199 priv = MEMO_PAGE_GET_PRIVATE (object);
200
201 if (priv->open_cancellable) {
202 g_cancellable_cancel (priv->open_cancellable);
203 g_object_unref (priv->open_cancellable);
204 priv->open_cancellable = NULL;
205 }
206
207 g_strfreev (priv->address_strings);
208 priv->address_strings = NULL;
209
210 g_free (priv->fallback_address);
211 priv->fallback_address = NULL;
212
213 /* Chain up to parent's dispose() method. */
214 G_OBJECT_CLASS (memo_page_parent_class)->dispose (object);
215 }
216
217 static void
218 memo_page_finalize (GObject *object)
219 {
220 MemoPagePrivate *priv;
221
222 priv = MEMO_PAGE_GET_PRIVATE (object);
223
224 if (priv->name_selector) {
225 e_name_selector_cancel_loading (priv->name_selector);
226 g_object_unref (priv->name_selector);
227 priv->name_selector = NULL;
228 }
229
230 if (priv->main != NULL) {
231 g_object_unref (priv->main);
232 priv->main = NULL;
233 }
234
235 if (priv->builder) {
236 g_object_unref (priv->builder);
237 priv->builder = NULL;
238 }
239
240 /* Chain up to parent's finalize() method. */
241 G_OBJECT_CLASS (memo_page_parent_class)->finalize (object);
242 }
243
244 static GtkWidget *
245 memo_page_get_widget (CompEditorPage *page)
246 {
247 MemoPagePrivate *priv = MEMO_PAGE_GET_PRIVATE (page);
248
249 return priv->main;
250 }
251
252 static void
253 memo_page_focus_main_widget (CompEditorPage *page)
254 {
255 MemoPagePrivate *priv = MEMO_PAGE_GET_PRIVATE (page);
256
257 gtk_widget_grab_focus (priv->summary_entry);
258 }
259
260 static gboolean
261 memo_page_fill_widgets (CompEditorPage *page,
262 ECalComponent *comp)
263 {
264 MemoPage *mpage;
265 MemoPagePrivate *priv;
266 CompEditor *editor;
267 CompEditorFlags flags;
268 ECalClient *client;
269 ECalComponentClassification cl;
270 ECalComponentText text;
271 ECalComponentDateTime d;
272 ESourceRegistry *registry;
273 EShell *shell;
274 GSList *l;
275 const gchar *categories;
276 gchar *backend_addr = NULL;
277
278 mpage = MEMO_PAGE (page);
279 priv = mpage->priv;
280
281 editor = comp_editor_page_get_editor (page);
282 client = comp_editor_get_client (editor);
283 flags = comp_editor_get_flags (editor);
284 shell = comp_editor_get_shell (editor);
285
286 registry = e_shell_get_registry (shell);
287
288 /* Clean the screen */
289 clear_widgets (mpage);
290
291 /* Summary */
292 e_cal_component_get_summary (comp, &text);
293 if (text.value != NULL)
294 gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), text.value);
295 else
296 gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), "");
297
298 e_cal_component_get_description_list (comp, &l);
299 if (l && l->data) {
300 ECalComponentText *dtext;
301
302 dtext = l->data;
303 gtk_text_buffer_set_text (
304 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)),
305 dtext->value ? dtext->value : "", -1);
306 } else {
307 gtk_text_buffer_set_text (
308 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)),
309 "", 0);
310 }
311 e_cal_component_free_text_list (l);
312 e_buffer_tagger_update_tags (GTK_TEXT_VIEW (priv->memo_content));
313
314 /* Start Date. */
315 e_cal_component_get_dtstart (comp, &d);
316 if (d.value) {
317 struct icaltimetype *start_tt = d.value;
318 e_date_edit_set_date (
319 E_DATE_EDIT (priv->start_date),
320 start_tt->year, start_tt->month,
321 start_tt->day);
322 } else if (!(flags & COMP_EDITOR_NEW_ITEM))
323 e_date_edit_set_time (E_DATE_EDIT (priv->start_date), -1);
324 e_cal_component_free_datetime (&d);
325
326 /* Classification. */
327 e_cal_component_get_classification (comp, &cl);
328 comp_editor_set_classification (editor, cl);
329
330 /* Categories */
331 e_cal_component_get_categories (comp, &categories);
332 if (categories != NULL)
333 gtk_entry_set_text (GTK_ENTRY (priv->categories), categories);
334 else
335 gtk_entry_set_text (GTK_ENTRY (priv->categories), "");
336
337 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
338 set_subscriber_info_string (mpage, backend_addr);
339
340 if (e_cal_component_has_organizer (comp)) {
341 ECalComponentOrganizer organizer;
342
343 e_cal_component_get_organizer (comp, &organizer);
344 if (organizer.value != NULL) {
345 const gchar *strip = itip_strip_mailto (organizer.value);
346 gchar *string;
347
348 if (organizer.cn != NULL)
349 string = g_strdup_printf ("%s <%s>", organizer.cn, strip);
350 else
351 string = g_strdup (strip);
352
353 if (itip_organizer_is_user (registry, comp, client) ||
354 itip_sentby_is_user (registry, comp, client)) {
355 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->org_combo))), string);
356 } else {
357 GtkComboBox *combo_box;
358 GtkListStore *list_store;
359 GtkTreeModel *model;
360 GtkTreeIter iter;
361
362 combo_box = GTK_COMBO_BOX (priv->org_combo);
363 model = gtk_combo_box_get_model (combo_box);
364 list_store = GTK_LIST_STORE (model);
365
366 gtk_list_store_clear (list_store);
367 gtk_list_store_append (list_store, &iter);
368 gtk_list_store_set (list_store, &iter, 0, string, -1);
369 gtk_combo_box_set_active (combo_box, 0);
370 gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->org_combo))), FALSE);
371 }
372 g_free (string);
373 }
374 }
375
376 if (backend_addr)
377 g_free (backend_addr);
378
379 /* Source */
380 e_source_combo_box_set_active (
381 E_SOURCE_COMBO_BOX (priv->source_combo_box),
382 e_client_get_source (E_CLIENT (client)));
383
384 if (priv->to_entry && (flags & COMP_EDITOR_IS_SHARED) && !(flags & COMP_EDITOR_NEW_ITEM))
385 gtk_entry_set_text (GTK_ENTRY (priv->to_entry), get_recipients (comp));
386
387 sensitize_widgets (mpage);
388
389 return TRUE;
390 }
391
392 static void
393 memo_page_class_init (MemoPageClass *class)
394 {
395 CompEditorPageClass *editor_page_class;
396 GObjectClass *object_class;
397
398 g_type_class_add_private (class, sizeof (MemoPagePrivate));
399
400 object_class = G_OBJECT_CLASS (class);
401 object_class->dispose = memo_page_dispose;
402 object_class->finalize = memo_page_finalize;
403
404 editor_page_class = COMP_EDITOR_PAGE_CLASS (class);
405 editor_page_class->get_widget = memo_page_get_widget;
406 editor_page_class->focus_main_widget = memo_page_focus_main_widget;
407 editor_page_class->fill_widgets = memo_page_fill_widgets;
408 editor_page_class->fill_component = memo_page_fill_component;
409 }
410
411 static void
412 memo_page_init (MemoPage *mpage)
413 {
414 mpage->priv = MEMO_PAGE_GET_PRIVATE (mpage);
415 }
416
417 /* returns whether changed info text */
418 static gboolean
419 check_starts_in_the_past (MemoPage *mpage)
420 {
421 MemoPagePrivate *priv;
422 struct icaltimetype start_tt = icaltime_null_time ();
423
424 if ((comp_editor_get_flags (comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage))) & COMP_EDITOR_NEW_ITEM) == 0)
425 return FALSE;
426
427 priv = mpage->priv;
428
429 start_tt.is_date = TRUE;
430 if (e_date_edit_get_date (E_DATE_EDIT (priv->start_date), &start_tt.year, &start_tt.month, &start_tt.day) &&
431 comp_editor_test_time_in_the_past (start_tt)) {
432 gchar *tmp = g_strconcat ("<b>", _("Memo's start date is in the past"), "</b>", NULL);
433 memo_page_set_info_string (mpage, GTK_STOCK_DIALOG_WARNING, tmp);
434 g_free (tmp);
435 } else {
436 memo_page_set_info_string (mpage, NULL, NULL);
437 }
438
439 return TRUE;
440 }
441
442 static void
443 sensitize_widgets (MemoPage *mpage)
444 {
445 GtkActionGroup *action_group;
446 gboolean read_only, sens = FALSE, sensitize;
447 CompEditor *editor;
448 CompEditorFlags flags;
449 MemoPagePrivate *priv;
450 ECalClient *client;
451
452 priv = mpage->priv;
453
454 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage));
455 client = comp_editor_get_client (editor);
456 flags = comp_editor_get_flags (editor);
457
458 read_only = e_client_is_readonly (E_CLIENT (client));
459
460 if (flags & COMP_EDITOR_IS_SHARED)
461 sens = flags & COMP_EDITOR_USER_ORG;
462 else
463 sens = TRUE;
464
465 sensitize = (!read_only && sens);
466
467 if (read_only) {
468 gchar *tmp = g_strconcat ("<b>", _("Memo cannot be edited, because the selected memo list is read only"), "</b>", NULL);
469 memo_page_set_info_string (mpage, GTK_STOCK_DIALOG_INFO, tmp);
470 g_free (tmp);
471 } else if (!sens) {
472 gchar *tmp = g_strconcat ("<b>", _("Memo cannot be fully edited, because you are not the organizer"), "</b>", NULL);
473 memo_page_set_info_string (mpage, GTK_STOCK_DIALOG_INFO, tmp);
474 g_free (tmp);
475 } else if (!check_starts_in_the_past (mpage)) {
476 memo_page_set_info_string (mpage, NULL, NULL);
477 }
478
479 /* The list of organizers is set to be non-editable. Otherwise any
480 * change in the displayed list causes an 'Account not found' error.
481 */
482 gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (priv->org_combo))), FALSE);
483
484 gtk_text_view_set_editable (GTK_TEXT_VIEW (priv->memo_content), sensitize);
485 gtk_widget_set_sensitive (priv->start_date, sensitize);
486 gtk_widget_set_sensitive (priv->categories_btn, !read_only);
487 gtk_editable_set_editable (GTK_EDITABLE (priv->categories), !read_only);
488 gtk_editable_set_editable (GTK_EDITABLE (priv->summary_entry), sensitize);
489
490 if (flags & COMP_EDITOR_IS_SHARED) {
491 if (priv->to_entry) {
492 gtk_editable_set_editable (GTK_EDITABLE (priv->to_entry), !read_only);
493 gtk_widget_grab_focus (priv->to_entry);
494 }
495 }
496
497 action_group = comp_editor_get_action_group (editor, "editable");
498 gtk_action_group_set_sensitive (action_group, !read_only);
499
500 action_group = comp_editor_get_action_group (editor, "individual");
501 gtk_action_group_set_sensitive (action_group, sensitize);
502 }
503
504 /* returns empty string rather than NULL because of simplicity of usage */
505 static const gchar *
506 get_recipients (ECalComponent *comp)
507 {
508 icalcomponent *icalcomp;
509 icalproperty *icalprop;
510
511 g_return_val_if_fail (comp != NULL, "");
512
513 icalcomp = e_cal_component_get_icalcomponent (comp);
514
515 /* first look if we have there such property */
516 for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
517 icalprop;
518 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
519 const gchar *xname = icalproperty_get_x_name (icalprop);
520
521 if (xname && strcmp (xname, "X-EVOLUTION-RECIPIENTS") == 0)
522 break;
523 }
524
525 if (icalprop)
526 return icalproperty_get_x (icalprop);
527
528 return "";
529 }
530
531 static gboolean
532 fill_comp_with_recipients (ENameSelector *name_selector,
533 ECalComponent *comp)
534 {
535 EDestinationStore *destination_store;
536 GString *str = NULL;
537 GList *l, *destinations;
538 ENameSelectorModel *name_selector_model = e_name_selector_peek_model (name_selector);
539 icalcomponent *icalcomp;
540 icalproperty *icalprop;
541
542 e_name_selector_model_peek_section (
543 name_selector_model, "To",
544 NULL, &destination_store);
545
546 destinations = e_destination_store_list_destinations (destination_store);
547 for (l = destinations; l; l = g_list_next (l)) {
548 EDestination *destination = l->data, *des = NULL;
549 const GList *list_dests = NULL, *l;
550 GList card_dest;
551
552 if (e_destination_is_evolution_list (destination)) {
553 list_dests = e_destination_list_get_dests (destination);
554 } else {
555 EContact *contact = e_destination_get_contact (destination);
556 /* check if the contact is contact list which is not expanded yet */
557 /* we expand it by getting the list again from the server forming the query */
558 if (contact && e_contact_get (contact , E_CONTACT_IS_LIST)) {
559 EBookClient *book_client = NULL;
560 ENameSelectorDialog *dialog;
561 ENameSelectorModel *model;
562 EContactStore *c_store;
563 GSList *clients, *l;
564 gchar *uid = e_contact_get (contact, E_CONTACT_BOOK_UID);
565
566 dialog = e_name_selector_peek_dialog (name_selector);
567 model = e_name_selector_dialog_peek_model (dialog);
568 c_store = e_name_selector_model_peek_contact_store (model);
569 clients = e_contact_store_get_clients (c_store);
570
571 for (l = clients; l; l = l->next) {
572 EBookClient *b = l->data;
573 ESource *source;
574
575 source = e_client_get_source (E_CLIENT (b));
576
577 if (g_strcmp0 (uid, e_source_get_uid (source)) == 0) {
578 book_client = b;
579 break;
580 }
581 }
582
583 if (book_client) {
584 GSList *contacts = NULL;
585 EContact *n_con = NULL;
586 gchar *query;
587
588 query = g_strdup_printf (
589 "(is \"full_name\" \"%s\")",
590 (gchar *) e_contact_get (contact, E_CONTACT_FULL_NAME));
591
592 if (!e_book_client_get_contacts_sync (book_client, query, &contacts, NULL, NULL)) {
593 g_warning ("Could not get contact from the book \n");
594 } else {
595 des = e_destination_new ();
596 n_con = contacts->data;
597
598 e_destination_set_contact (des, n_con, 0);
599 e_destination_set_client (des, book_client);
600 list_dests = e_destination_list_get_dests (des);
601
602 g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
603 g_slist_free (contacts);
604 }
605
606 g_free (query);
607 }
608 g_slist_free (clients);
609 } else {
610 card_dest.next = NULL;
611 card_dest.prev = NULL;
612 card_dest.data = destination;
613 list_dests = &card_dest;
614 }
615 }
616
617 for (l = list_dests; l; l = l->next) {
618 EDestination *dest = l->data;
619 const gchar *attendee = NULL;
620
621 /* If we couldn't get the attendee prior,
622 * get the email address as the default. */
623 if (attendee == NULL || *attendee == '\0')
624 attendee = e_destination_get_email (dest);
625
626 if (attendee == NULL || *attendee == '\0')
627 continue;
628
629 if (!str) {
630 str = g_string_new (NULL);
631 g_string_prepend (str, attendee);
632 continue;
633 }
634 g_string_prepend_c (str, ';');
635 g_string_prepend (str, attendee);
636 }
637 }
638
639 g_list_free (destinations);
640
641 if (str && *str->str) {
642 icalcomp = e_cal_component_get_icalcomponent (comp);
643 icalprop = icalproperty_new_x (str->str);
644 icalproperty_set_x_name (icalprop, "X-EVOLUTION-RECIPIENTS");
645 icalcomponent_add_property (icalcomp, icalprop);
646
647 g_string_free (str, FALSE);
648 return TRUE;
649 } else
650 return FALSE;
651 }
652
653 /* fill_component handler for the memo page */
654 static gboolean
655 memo_page_fill_component (CompEditorPage *page,
656 ECalComponent *comp)
657 {
658 MemoPage *mpage;
659 MemoPagePrivate *priv;
660 CompEditor *editor;
661 CompEditorFlags flags;
662 ECalClient *client;
663 ECalComponentClassification classification;
664 ECalComponentDateTime start_date;
665 struct icaltimetype start_tt;
666 gchar *cat, *str;
667 gint i;
668 GtkTextBuffer *text_buffer;
669 GtkTextIter text_iter_start, text_iter_end;
670
671 mpage = MEMO_PAGE (page);
672 priv = mpage->priv;
673
674 editor = comp_editor_page_get_editor (page);
675 client = comp_editor_get_client (editor);
676 flags = comp_editor_get_flags (editor);
677
678 text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content));
679
680 /* Summary */
681 str = gtk_editable_get_chars (GTK_EDITABLE (priv->summary_entry), 0, -1);
682 if (str == NULL || *str == '\0')
683 e_cal_component_set_summary (comp, NULL);
684 else {
685 ECalComponentText text;
686
687 text.value = str;
688 text.altrep = NULL;
689
690 e_cal_component_set_summary (comp, &text);
691 }
692
693 g_free (str);
694 str = NULL;
695
696 /* Memo Content */
697
698 gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start);
699 gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
700 str = gtk_text_buffer_get_text (text_buffer, &text_iter_start, &text_iter_end, FALSE);
701
702 if (!str || strlen (str) == 0) {
703 e_cal_component_set_description_list (comp, NULL);
704 }
705 else {
706 GSList l;
707 ECalComponentText text;
708 gchar *p;
709 gunichar uc;
710
711 for (i = 0, p = str, uc = g_utf8_get_char_validated (p, -1);
712 i < 50 && p && uc < (gunichar) - 2;
713 i++, p = g_utf8_next_char (p), uc = g_utf8_get_char_validated (p, -1)) {
714 if (uc == '\n' || !uc) {
715 p = NULL;
716 break;
717 }
718 }
719
720 text.value = str;
721 text.altrep = NULL;
722 l.data = &text;
723 l.next = NULL;
724
725 e_cal_component_set_description_list (comp, &l);
726 }
727
728 g_free (str);
729
730 /* Dates */
731 start_tt = icaltime_null_time ();
732 start_tt.is_date = 1;
733 start_date.value = &start_tt;
734 start_date.tzid = NULL;
735
736 if (!e_date_edit_date_is_valid (E_DATE_EDIT (priv->start_date))) {
737 comp_editor_page_display_validation_error (page, _("Start date is wrong"), priv->start_date);
738 return FALSE;
739 }
740 if (e_date_edit_get_date (E_DATE_EDIT (priv->start_date),
741 &start_tt.year,
742 &start_tt.month,
743 &start_tt.day))
744 e_cal_component_set_dtstart (comp, &start_date);
745 else
746 e_cal_component_set_dtstart (comp, NULL);
747
748 /* Classification. */
749 classification = comp_editor_get_classification (editor);
750 e_cal_component_set_classification (comp, classification);
751
752 /* Categories */
753 cat = gtk_editable_get_chars (GTK_EDITABLE (priv->categories), 0, -1);
754 str = comp_editor_strip_categories (cat);
755 g_free (cat);
756
757 e_cal_component_set_categories (comp, str);
758
759 g_free (str);
760
761 /* change recipients only when creating new item, after that no such action is available */
762 if ((flags & COMP_EDITOR_IS_SHARED) && (flags & COMP_EDITOR_NEW_ITEM) && fill_comp_with_recipients (priv->name_selector, comp)) {
763 ECalComponentOrganizer organizer = {NULL, NULL, NULL, NULL};
764
765 gchar *backend_addr = NULL;
766 gchar *backend_mailto = NULL;
767 gchar *name = NULL;
768 gchar *mailto = NULL;
769
770 e_client_get_backend_property_sync (E_CLIENT (client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
771
772 /* Find the identity for the organizer or sentby field */
773 if (!get_current_identity (mpage, &name, &mailto)) {
774 e_notice (
775 priv->main, GTK_MESSAGE_ERROR,
776 _("An organizer is required."));
777 return FALSE;
778 }
779
780 /* Prefer the backend addres if we have one. */
781 if (backend_addr != NULL && *backend_addr != '\0') {
782 backend_mailto = g_strdup_printf (
783 "MAILTO:%s", backend_addr);
784 if (g_ascii_strcasecmp (backend_mailto, mailto) == 0) {
785 g_free (backend_mailto);
786 backend_mailto = NULL;
787 }
788 }
789
790 if (backend_mailto == NULL) {
791 organizer.cn = name;
792 organizer.value = mailto;
793 name = mailto = NULL;
794 } else {
795 organizer.value = backend_mailto;
796 organizer.sentby = mailto;
797 backend_mailto = mailto = NULL;
798 }
799
800 e_cal_component_set_organizer (comp, &organizer);
801
802 if (flags & COMP_EDITOR_NEW_ITEM)
803 comp_editor_set_needs_send (editor, TRUE);
804
805 g_free (backend_addr);
806 g_free (backend_mailto);
807 g_free (name);
808 g_free (mailto);
809 }
810
811 comp_editor_set_needs_send (editor, (flags & COMP_EDITOR_IS_SHARED) != 0 &&
812 itip_organizer_is_user (e_shell_get_registry (comp_editor_get_shell (editor)), comp, client));
813
814 return TRUE;
815 }
816
817 void
818 memo_page_set_show_categories (MemoPage *page,
819 gboolean state)
820 {
821 if (state) {
822 gtk_widget_show (page->priv->categories_btn);
823 gtk_widget_show (page->priv->categories);
824 } else {
825 gtk_widget_hide (page->priv->categories_btn);
826 gtk_widget_hide (page->priv->categories);
827 }
828 }
829
830 /*If the msg has some value set, the icon should always be set */
831 void
832 memo_page_set_info_string (MemoPage *mpage,
833 const gchar *icon,
834 const gchar *msg)
835 {
836 MemoPagePrivate *priv;
837
838 priv = mpage->priv;
839
840 gtk_image_set_from_stock (GTK_IMAGE (priv->info_icon), icon, GTK_ICON_SIZE_BUTTON);
841 gtk_label_set_markup (GTK_LABEL (priv->info_string), msg);
842
843 if (msg && icon)
844 gtk_widget_show (priv->info_hbox);
845 else
846 gtk_widget_hide (priv->info_hbox);
847 }
848
849 /* Gets the widgets from the XML file and returns if they are all available. */
850 static gboolean
851 get_widgets (MemoPage *mpage)
852 {
853 EShell *shell;
854 ESourceRegistry *registry;
855 CompEditor *editor;
856 CompEditorPage *page = COMP_EDITOR_PAGE (mpage);
857 GtkEntryCompletion *completion;
858 MemoPagePrivate *priv;
859 GSList *accel_groups;
860 GtkWidget *toplevel;
861 GtkWidget *parent;
862
863 priv = mpage->priv;
864
865 #define GW(name) e_builder_get_widget (priv->builder, name)
866
867 editor = comp_editor_page_get_editor (page);
868 shell = comp_editor_get_shell (editor);
869 registry = e_shell_get_registry (shell);
870
871 priv->main = GW ("memo-page");
872 if (!priv->main) {
873 g_warning ("couldn't find memo-page!");
874 return FALSE;
875 }
876
877 /* Get the GtkAccelGroup from the toplevel window, so we can install
878 * it when the notebook page is mapped. */
879 toplevel = gtk_widget_get_toplevel (priv->main);
880 accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel));
881 if (accel_groups)
882 page->accel_group = g_object_ref (accel_groups->data);
883
884 g_object_ref (priv->main);
885 parent = gtk_widget_get_parent (priv->main);
886 gtk_container_remove (GTK_CONTAINER (parent), priv->main);
887
888 priv->info_hbox = GW ("generic-info");
889 priv->info_icon = GW ("generic-info-image");
890 priv->info_string = GW ("generic-info-msgs");
891
892 priv->org_label = GW ("org-label");
893 priv->org_combo = GW ("org-combo");
894 gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (priv->org_combo))));
895 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->org_combo), 0);
896
897 priv->to_button = GW ("to-button");
898 priv->to_hbox = GW ("to-hbox");
899
900 priv->summary_label = GW ("sum-label");
901 priv->summary_entry = GW ("sum-entry");
902
903 priv->start_label = GW ("start-label");
904 priv->start_date = GW ("start-date");
905
906 priv->memo_content = GW ("memo_content");
907
908 priv->categories_btn = GW ("categories-button");
909 priv->categories = GW ("categories");
910
911 priv->source_combo_box = GW ("source");
912 e_source_combo_box_set_registry (
913 E_SOURCE_COMBO_BOX (priv->source_combo_box), registry);
914 #undef GW
915
916 completion = e_category_completion_new ();
917 gtk_entry_set_completion (GTK_ENTRY (priv->categories), completion);
918 g_object_unref (completion);
919
920 if (priv->summary_entry) {
921 EShell *shell;
922 EShellSettings *shell_settings;
923 CompEditor *editor;
924
925 editor = comp_editor_page_get_editor (page);
926 shell = comp_editor_get_shell (editor);
927 shell_settings = e_shell_get_shell_settings (shell);
928
929 g_object_bind_property (
930 shell_settings, "composer-inline-spelling",
931 priv->summary_entry, "checking-enabled",
932 G_BINDING_SYNC_CREATE);
933 }
934
935 return (priv->memo_content
936 && priv->categories_btn
937 && priv->categories
938 && priv->start_date);
939 }
940
941 /* Callback used when the categories button is clicked; we must bring up the
942 * category list dialog.
943 */
944 static void
945 categories_clicked_cb (GtkWidget *button,
946 MemoPage *mpage)
947 {
948 GtkEntry *entry;
949
950 entry = GTK_ENTRY (mpage->priv->categories);
951 e_categories_config_open_dialog_for_entry (entry);
952 }
953
954 static void
955 mpage_client_opened_cb (GObject *source_object,
956 GAsyncResult *result,
957 gpointer user_data)
958 {
959 ESource *source = E_SOURCE (source_object);
960 EClient *client = NULL;
961 MemoPage *mpage = user_data;
962 MemoPagePrivate *priv;
963 CompEditor *editor;
964 GError *error = NULL;
965
966 if (!e_client_utils_open_new_finish (source, result, &client, &error)) {
967 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
968 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
969 g_clear_error (&error);
970 return;
971 }
972 }
973
974 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage));
975 priv = mpage->priv;
976
977 if (error != NULL) {
978 GtkWidget *dialog;
979 ECalClient *old_client;
980
981 old_client = comp_editor_get_client (editor);
982
983 e_source_combo_box_set_active (
984 E_SOURCE_COMBO_BOX (priv->source_combo_box),
985 e_client_get_source (E_CLIENT (old_client)));
986
987 dialog = gtk_message_dialog_new (
988 NULL, GTK_DIALOG_MODAL,
989 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
990 _("Unable to open memos in '%s': %s"),
991 e_source_get_display_name (source),
992 error->message);
993 gtk_dialog_run (GTK_DIALOG (dialog));
994 gtk_widget_destroy (dialog);
995
996 g_clear_error (&error);
997 } else {
998 icaltimezone *zone;
999 CompEditorFlags flags;
1000 ECalClient *cal_client = E_CAL_CLIENT (client);
1001
1002 g_return_if_fail (cal_client != NULL);
1003
1004 flags = comp_editor_get_flags (editor);
1005 zone = comp_editor_get_timezone (editor);
1006 e_cal_client_set_default_timezone (cal_client, zone);
1007
1008 comp_editor_set_client (editor, cal_client);
1009
1010 if (client) {
1011 gchar *backend_addr = NULL;
1012
1013 e_client_get_backend_property_sync (client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &backend_addr, NULL, NULL);
1014
1015 if (flags & COMP_EDITOR_IS_SHARED)
1016 memo_page_select_organizer (mpage, backend_addr);
1017
1018 set_subscriber_info_string (mpage, backend_addr);
1019 g_free (backend_addr);
1020 }
1021
1022 sensitize_widgets (mpage);
1023 }
1024 }
1025
1026 static void
1027 source_changed_cb (ESourceComboBox *source_combo_box,
1028 MemoPage *mpage)
1029 {
1030 MemoPagePrivate *priv = mpage->priv;
1031 ESource *source;
1032
1033 if (comp_editor_page_get_updating (COMP_EDITOR_PAGE (mpage)))
1034 return;
1035
1036 source = e_source_combo_box_ref_active (source_combo_box);
1037 g_return_if_fail (source != NULL);
1038
1039 if (priv->open_cancellable) {
1040 g_cancellable_cancel (priv->open_cancellable);
1041 g_object_unref (priv->open_cancellable);
1042 }
1043 priv->open_cancellable = g_cancellable_new ();
1044
1045 e_client_utils_open_new (
1046 source, E_CLIENT_SOURCE_TYPE_MEMOS,
1047 FALSE, priv->open_cancellable,
1048 mpage_client_opened_cb, mpage);
1049
1050 g_object_unref (source);
1051 }
1052
1053 static void
1054 set_subscriber_info_string (MemoPage *mpage,
1055 const gchar *backend_address)
1056 {
1057 if (!check_starts_in_the_past (mpage))
1058 memo_page_set_info_string (mpage, NULL, NULL);
1059 }
1060
1061 static void
1062 summary_changed_cb (GtkEntry *entry,
1063 CompEditorPage *page)
1064 {
1065 CompEditor *editor;
1066 const gchar *text;
1067
1068 if (comp_editor_page_get_updating (page))
1069 return;
1070
1071 editor = comp_editor_page_get_editor (page);
1072 text = gtk_entry_get_text (entry);
1073 comp_editor_set_summary (editor, text);
1074 }
1075
1076 static void
1077 to_button_clicked_cb (GtkButton *button,
1078 MemoPage *mpage)
1079 {
1080 e_name_selector_show_dialog (
1081 mpage->priv->name_selector,
1082 mpage->priv->main);
1083 }
1084
1085 static void
1086 memo_page_start_date_changed_cb (MemoPage *mpage)
1087 {
1088 check_starts_in_the_past (mpage);
1089 comp_editor_page_changed (COMP_EDITOR_PAGE (mpage));
1090 }
1091
1092 /* Hooks the widget signals */
1093 static gboolean
1094 init_widgets (MemoPage *mpage)
1095 {
1096 CompEditor *editor;
1097 MemoPagePrivate *priv = mpage->priv;
1098 GtkTextBuffer *buffer;
1099 GtkTextView *view;
1100 GtkAction *action;
1101 gboolean active;
1102
1103 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage));
1104
1105 /* Generic informative messages */
1106 gtk_widget_hide (priv->info_hbox);
1107
1108 /* Summary */
1109 g_signal_connect (
1110 priv->summary_entry, "changed",
1111 G_CALLBACK (summary_changed_cb), mpage);
1112
1113 /* Memo Content */
1114 view = GTK_TEXT_VIEW (priv->memo_content);
1115 buffer = gtk_text_view_get_buffer (view);
1116 gtk_text_view_set_wrap_mode (view, GTK_WRAP_WORD);
1117 e_buffer_tagger_connect (view);
1118
1119 /* Categories button */
1120 g_signal_connect (
1121 priv->categories_btn, "clicked",
1122 G_CALLBACK (categories_clicked_cb), mpage);
1123
1124 /* Source selector */
1125 g_signal_connect (
1126 priv->source_combo_box, "changed",
1127 G_CALLBACK (source_changed_cb), mpage);
1128
1129 /* Connect the default signal handler to use to make sure the "changed"
1130 * field gets set whenever a field is changed. */
1131
1132 /* Belongs to priv->memo_content */
1133 g_signal_connect_swapped (
1134 buffer, "changed",
1135 G_CALLBACK (comp_editor_page_changed), mpage);
1136
1137 g_signal_connect_swapped (
1138 priv->categories, "changed",
1139 G_CALLBACK (comp_editor_page_changed), mpage);
1140
1141 g_signal_connect_swapped (
1142 priv->summary_entry, "changed",
1143 G_CALLBACK (comp_editor_page_changed), mpage);
1144
1145 g_signal_connect_swapped (
1146 priv->source_combo_box, "changed",
1147 G_CALLBACK (comp_editor_page_changed), mpage);
1148
1149 g_signal_connect_swapped (
1150 priv->start_date, "changed",
1151 G_CALLBACK (memo_page_start_date_changed_cb), mpage);
1152
1153 if (priv->name_selector) {
1154 ENameSelectorDialog *name_selector_dialog;
1155
1156 name_selector_dialog = e_name_selector_peek_dialog (priv->name_selector);
1157
1158 g_signal_connect (
1159 name_selector_dialog, "response",
1160 G_CALLBACK (gtk_widget_hide), NULL);
1161 g_signal_connect (
1162 priv->to_button, "clicked",
1163 G_CALLBACK (to_button_clicked_cb), mpage);
1164 g_signal_connect_swapped (
1165 priv->to_entry, "changed",
1166 G_CALLBACK (comp_editor_page_changed), mpage);
1167 }
1168
1169 action = comp_editor_get_action (editor, "view-categories");
1170 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1171 memo_page_set_show_categories (mpage, active);
1172
1173 return TRUE;
1174 }
1175
1176 static GtkWidget *
1177 get_to_entry (ENameSelector *name_selector)
1178 {
1179 ENameSelectorModel *name_selector_model;
1180 ENameSelectorEntry *name_selector_entry;
1181
1182 name_selector_model = e_name_selector_peek_model (name_selector);
1183 e_name_selector_model_add_section (name_selector_model, "To", _("To"), NULL);
1184 name_selector_entry = (ENameSelectorEntry *) e_name_selector_peek_section_list (name_selector, "To");
1185
1186 return GTK_WIDGET (name_selector_entry);
1187 }
1188
1189 static void
1190 memo_page_select_organizer (MemoPage *mpage,
1191 const gchar *backend_address)
1192 {
1193 MemoPagePrivate *priv = mpage->priv;
1194 CompEditor *editor;
1195 CompEditorFlags flags;
1196 const gchar *default_address;
1197 gint ii;
1198
1199 /* Treat an empty backend address as NULL. */
1200 if (backend_address != NULL && *backend_address == '\0')
1201 backend_address = NULL;
1202
1203 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage));
1204 flags = comp_editor_get_flags (editor);
1205
1206 default_address = priv->fallback_address;
1207
1208 if (backend_address != NULL) {
1209 for (ii = 0; priv->address_strings[ii] != NULL; ii++) {
1210 if (g_strrstr (priv->address_strings[ii], backend_address) != NULL) {
1211 default_address = priv->address_strings[ii];
1212 break;
1213 }
1214 }
1215 }
1216
1217 if (default_address != NULL) {
1218 if (flags & COMP_EDITOR_NEW_ITEM) {
1219 gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->org_combo))), default_address);
1220 }
1221 } else
1222 g_warning ("No potential organizers!");
1223 }
1224
1225 /**
1226 * memo_page_construct:
1227 * @mpage: An memo page.
1228 *
1229 * Constructs an memo page by loading its Glade data.
1230 *
1231 * Return value: The same object as @mpage, or NULL if the widgets could not be
1232 * created.
1233 **/
1234 MemoPage *
1235 memo_page_construct (MemoPage *mpage)
1236 {
1237 MemoPagePrivate *priv;
1238 EShell *shell;
1239 CompEditor *editor;
1240 CompEditorFlags flags;
1241 ESourceRegistry *registry;
1242
1243 priv = mpage->priv;
1244
1245 editor = comp_editor_page_get_editor (COMP_EDITOR_PAGE (mpage));
1246
1247 flags = comp_editor_get_flags (editor);
1248 shell = comp_editor_get_shell (editor);
1249
1250 registry = e_shell_get_registry (shell);
1251
1252 /* Make sure our custom widget classes are registered with
1253 * GType before we load the GtkBuilder definition file. */
1254 E_TYPE_DATE_EDIT;
1255 E_TYPE_SPELL_ENTRY;
1256
1257 priv->builder = gtk_builder_new ();
1258 e_load_ui_builder_definition (priv->builder, "memo-page.ui");
1259
1260 if (!get_widgets (mpage)) {
1261 g_message (
1262 "memo_page_construct(): "
1263 "Could not find all widgets in the XML file!");
1264 return NULL;
1265 }
1266
1267 if (flags & COMP_EDITOR_IS_SHARED) {
1268 GtkComboBox *combo_box;
1269 GtkListStore *list_store;
1270 GtkTreeModel *model;
1271 GtkTreeIter iter;
1272 gint ii;
1273
1274 combo_box = GTK_COMBO_BOX (priv->org_combo);
1275 model = gtk_combo_box_get_model (combo_box);
1276 list_store = GTK_LIST_STORE (model);
1277
1278 priv->address_strings = itip_get_user_identities (registry);
1279 priv->fallback_address = itip_get_fallback_identity (registry);
1280
1281 /* FIXME Could we just use a GtkComboBoxText? */
1282 for (ii = 0; priv->address_strings[ii] != NULL; ii++) {
1283 gtk_list_store_append (list_store, &iter);
1284 gtk_list_store_set (
1285 list_store, &iter,
1286 0, priv->address_strings[ii], -1);
1287 }
1288
1289 gtk_combo_box_set_active (combo_box, 0);
1290
1291 gtk_widget_show (priv->org_label);
1292 gtk_widget_show (priv->org_combo);
1293
1294 priv->name_selector = e_name_selector_new (registry);
1295 priv->to_entry = get_to_entry (priv->name_selector);
1296 gtk_container_add ((GtkContainer *) priv->to_hbox, priv->to_entry);
1297 gtk_widget_show (priv->to_hbox);
1298 gtk_widget_show (priv->to_entry);
1299 gtk_widget_show (priv->to_button);
1300
1301 if (!(flags & COMP_EDITOR_NEW_ITEM)) {
1302 gtk_widget_set_sensitive (priv->to_button, FALSE);
1303 gtk_widget_set_sensitive (priv->to_entry, FALSE);
1304 }
1305 }
1306
1307 if (!init_widgets (mpage)) {
1308 g_message ("memo_page_construct(): "
1309 "Could not initialize the widgets!");
1310 return NULL;
1311 }
1312
1313 return mpage;
1314 }
1315
1316 /**
1317 * memo_page_new:
1318 *
1319 * Creates a new memo page.
1320 *
1321 * Return value: A newly-created task page, or NULL if the page could
1322 * not be created.
1323 **/
1324 MemoPage *
1325 memo_page_new (CompEditor *editor)
1326 {
1327 MemoPage *mpage;
1328
1329 g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
1330
1331 mpage = g_object_new (TYPE_MEMO_PAGE, "editor", editor, NULL);
1332
1333 if (!memo_page_construct (mpage)) {
1334 g_object_unref (mpage);
1335 g_return_val_if_reached (NULL);
1336 }
1337
1338 return mpage;
1339 }