Bug 792781 - 'Go to Folder' incorrectly unrefs CamelFolder twice
[evolution.git] / src / mail / e-mail-notes.c
blob54aa982e36df60ad7f7908de3a09af6a15ae8534
1 /*
2 * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-config.h"
20 #include <string.h>
21 #include <stdio.h>
23 #include <glib.h>
24 #include <glib/gi18n-lib.h>
25 #include <gtk/gtk.h>
27 #include <camel/camel.h>
28 #include <e-util/e-util.h>
29 #include <libemail-engine/libemail-engine.h>
31 #include "e-mail-notes.h"
33 #define E_TYPE_MAIL_NOTES_EDITOR \
34 (e_mail_notes_editor_get_type ())
35 #define E_MAIL_NOTES_EDITOR(obj) \
36 (G_TYPE_CHECK_INSTANCE_CAST \
37 ((obj), E_TYPE_MAIL_NOTES_EDITOR, EMailNotesEditor))
38 #define E_IS_MAIL_NOTES_EDITOR(obj) \
39 (G_TYPE_CHECK_INSTANCE_TYPE \
40 ((obj), E_TYPE_MAIL_NOTES_EDITOR))
42 typedef struct _EMailNotesEditor EMailNotesEditor;
43 typedef struct _EMailNotesEditorClass EMailNotesEditorClass;
45 struct _EMailNotesEditor {
46 GtkWindow parent;
48 EHTMLEditor *editor; /* not referenced */
49 EAttachmentPaned *attachment_paned; /* not referenced */
50 EFocusTracker *focus_tracker;
51 GtkActionGroup *action_group;
53 gboolean had_message;
54 CamelMimeMessage *message;
55 CamelFolder *folder;
56 gchar *uid;
59 struct _EMailNotesEditorClass {
60 GtkWindowClass parent_class;
63 GType e_mail_notes_editor_get_type (void);
65 G_DEFINE_TYPE (EMailNotesEditor, e_mail_notes_editor, GTK_TYPE_WINDOW)
67 static gchar *
68 e_mail_notes_extract_text_content (CamelMimePart *part)
70 CamelDataWrapper *content;
71 CamelStream *stream;
72 GByteArray *byte_array;
73 gchar *text = NULL;
75 g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
77 content = camel_medium_get_content (CAMEL_MEDIUM (part));
78 g_return_val_if_fail (content != NULL, NULL);
80 stream = camel_stream_mem_new ();
81 camel_data_wrapper_decode_to_stream_sync (content, stream, NULL, NULL);
82 camel_stream_close (stream, NULL, NULL);
84 byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
86 if (byte_array->data)
87 text = g_strndup ((const gchar *) byte_array->data, byte_array->len);
89 g_object_unref (stream);
91 return text;
94 static void
95 e_mail_notes_extract_text_from_multipart_alternative (EContentEditor *cnt_editor,
96 CamelMultipart *in_multipart)
98 guint ii, nparts;
100 g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
101 g_return_if_fail (CAMEL_IS_MULTIPART (in_multipart));
103 nparts = camel_multipart_get_number (in_multipart);
105 for (ii = 0; ii < nparts; ii++) {
106 CamelMimePart *part;
107 CamelContentType *ct;
109 /* Traverse from the end, where the best format available is stored */
110 part = camel_multipart_get_part (in_multipart, nparts - ii - 1);
111 if (!part)
112 continue;
114 ct = camel_mime_part_get_content_type (part);
115 if (!ct)
116 continue;
118 if (camel_content_type_is (ct, "text", "html")) {
119 gchar *text;
121 text = e_mail_notes_extract_text_content (part);
122 if (text) {
123 e_content_editor_set_html_mode (cnt_editor, TRUE);
124 e_content_editor_insert_content (
125 cnt_editor,
126 text,
127 E_CONTENT_EDITOR_INSERT_TEXT_HTML |
128 E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
129 g_free (text);
130 break;
132 } else if (camel_content_type_is (ct, "text", "plain")) {
133 gchar *text;
135 text = e_mail_notes_extract_text_content (part);
136 if (text) {
137 e_content_editor_insert_content (
138 cnt_editor,
139 text,
140 E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
141 E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
142 g_free (text);
144 break;
149 static void
150 e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes_editor,
151 CamelMultipart *multipart)
153 EContentEditor *cnt_editor;
154 guint ii, nparts;
156 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
157 g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
159 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
160 nparts = camel_multipart_get_number (multipart);
162 for (ii = 0; ii < nparts; ii++) {
163 CamelMimePart *part;
164 CamelContentType *ct;
165 CamelDataWrapper *content;
167 part = camel_multipart_get_part (multipart, ii);
168 if (!part)
169 continue;
171 ct = camel_mime_part_get_content_type (part);
172 if (!ct)
173 continue;
175 if (camel_content_type_is (ct, "image", "*")) {
176 e_content_editor_insert_image_from_mime_part (cnt_editor, part);
177 } else if (camel_content_type_is (ct, "multipart", "alternative")) {
178 content = camel_medium_get_content (CAMEL_MEDIUM (part));
179 if (CAMEL_IS_MULTIPART (content))
180 e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, CAMEL_MULTIPART (content));
185 static void
186 e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
187 CamelMimePart *part)
189 CamelContentType *ct;
190 CamelDataWrapper *content;
191 EContentEditor *cnt_editor;
193 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
194 g_return_if_fail (CAMEL_IS_MIME_PART (part));
196 content = camel_medium_get_content (CAMEL_MEDIUM (part));
197 ct = camel_data_wrapper_get_mime_type_field (content);
199 g_return_if_fail (content != NULL);
200 g_return_if_fail (ct != NULL);
202 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
204 if (camel_content_type_is (ct, "multipart", "related")) {
205 g_return_if_fail (CAMEL_IS_MULTIPART (content));
207 e_mail_notes_editor_extract_text_from_multipart_related (notes_editor, CAMEL_MULTIPART (content));
208 } else if (camel_content_type_is (ct, "multipart", "alternative")) {
209 if (CAMEL_IS_MULTIPART (content)) {
210 e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, CAMEL_MULTIPART (content));
212 } else if (camel_content_type_is (ct, "text", "plain")) {
213 gchar *text;
215 text = e_mail_notes_extract_text_content (part);
216 if (text) {
217 e_content_editor_insert_content (
218 cnt_editor,
219 text,
220 E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
221 E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
222 g_free (text);
227 static void
228 e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
229 CamelMimeMessage *message)
231 CamelContentType *ct;
232 CamelDataWrapper *content;
233 EContentEditor *cnt_editor;
235 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
236 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
238 content = camel_medium_get_content (CAMEL_MEDIUM (message));
239 ct = camel_data_wrapper_get_mime_type_field (content);
241 g_return_if_fail (content != NULL);
242 g_return_if_fail (ct != NULL);
244 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
246 if (camel_content_type_is (ct, "multipart", "mixed")) {
247 EAttachmentStore *attachment_store;
248 CamelMultipart *multipart;
249 guint ii, nparts;
251 g_return_if_fail (CAMEL_IS_MULTIPART (content));
253 attachment_store = e_attachment_view_get_store (E_ATTACHMENT_VIEW (notes_editor->attachment_paned));
254 multipart = CAMEL_MULTIPART (content);
255 nparts = camel_multipart_get_number (multipart);
257 /* The first part is the note text, the rest are attachments */
258 for (ii = 0; ii < nparts; ii++) {
259 CamelMimePart *part;
261 part = camel_multipart_get_part (multipart, ii);
262 if (!part)
263 continue;
265 ct = camel_mime_part_get_content_type (part);
266 if (!ct)
267 continue;
269 if (ii == 0) {
270 e_mail_notes_editor_extract_text_from_part (notes_editor, part);
271 } else {
272 EAttachment *attachment;
274 attachment = e_attachment_new ();
276 e_attachment_set_mime_part (attachment, part);
277 e_attachment_store_add_attachment (attachment_store, attachment);
278 e_attachment_load_async (attachment, (GAsyncReadyCallback)
279 e_attachment_load_handle_error, notes_editor);
281 g_object_unref (attachment);
284 } else {
285 e_mail_notes_editor_extract_text_from_part (notes_editor, CAMEL_MIME_PART (message));
288 e_content_editor_set_changed (cnt_editor, FALSE);
291 static CamelMimeMessage *
292 e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
294 EContentEditor *cnt_editor;
295 EAttachmentStore *attachment_store;
296 CamelMimeMessage *message = NULL;
297 gchar *message_uid;
298 const gchar *username;
299 CamelInternetAddress *address;
300 gboolean has_text = FALSE, has_attachments;
302 g_return_val_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor), NULL);
303 g_return_val_if_fail (notes_editor->editor, NULL);
305 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
306 g_return_val_if_fail (E_IS_CONTENT_EDITOR (cnt_editor), NULL);
308 message = camel_mime_message_new ();
309 username = g_get_user_name ();
310 if (!username || !*username)
311 username = g_get_real_name ();
312 address = camel_internet_address_new ();
313 camel_internet_address_add (address, NULL, username);
315 message_uid = camel_header_msgid_generate (g_get_host_name ());
317 camel_mime_message_set_from (message, address);
318 camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
319 camel_mime_message_set_subject (message, _("Message Note"));
320 camel_mime_message_set_message_id (message, message_uid);
322 g_object_unref (address);
323 g_free (message_uid);
325 attachment_store = e_attachment_view_get_store (E_ATTACHMENT_VIEW (notes_editor->attachment_paned));
326 has_attachments = e_attachment_store_get_num_attachments (attachment_store) > 0;
328 if (e_content_editor_get_html_mode (cnt_editor)) {
329 CamelMultipart *multipart_alternative;
330 CamelMultipart *multipart_body;
331 CamelMimePart *part;
332 GSList *inline_images_parts = NULL;
333 gchar *text;
335 multipart_alternative = camel_multipart_new ();
336 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_alternative), "multipart/alternative");
337 camel_multipart_set_boundary (multipart_alternative, NULL);
339 text = e_content_editor_get_content (
340 cnt_editor,
341 E_CONTENT_EDITOR_GET_TEXT_PLAIN |
342 E_CONTENT_EDITOR_GET_PROCESSED,
343 NULL, NULL);
345 if (text && *text) {
346 part = camel_mime_part_new ();
347 camel_mime_part_set_content (part, text, strlen (text), "text/plain");
348 camel_multipart_add_part (multipart_alternative, part);
350 g_object_unref (part);
352 has_text = TRUE;
355 g_free (text);
357 text = e_content_editor_get_content (
358 cnt_editor,
359 E_CONTENT_EDITOR_GET_PROCESSED |
360 E_CONTENT_EDITOR_GET_TEXT_HTML |
361 E_CONTENT_EDITOR_GET_INLINE_IMAGES,
362 g_get_host_name (),
363 &inline_images_parts);
365 if (has_attachments && !has_text && (!text || !*text)) {
366 /* Text is required, thus if there are attachments,
367 but no text, then store at least a space. */
368 g_free (text);
369 text = g_strdup (" ");
372 if (text && *text) {
373 part = camel_mime_part_new ();
374 camel_mime_part_set_content (part, text, strlen (text), "text/html");
375 camel_multipart_add_part (multipart_alternative, part);
377 g_object_unref (part);
379 has_text = TRUE;
380 } else {
381 g_slist_free_full (inline_images_parts, g_object_unref);
382 inline_images_parts = NULL;
385 g_free (text);
387 if (inline_images_parts) {
388 GSList *link;
390 multipart_body = camel_multipart_new ();
391 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_body), "multipart/related");
392 camel_multipart_set_boundary (multipart_body, NULL);
394 part = camel_mime_part_new ();
395 camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (multipart_alternative));
396 camel_multipart_add_part (multipart_body, part);
397 g_object_unref (part);
399 for (link = inline_images_parts; link; link = g_slist_next (link)) {
400 CamelMimePart *part = link->data;
402 if (!part)
403 continue;
405 camel_multipart_add_part (multipart_body, part);
407 } else {
408 multipart_body = multipart_alternative;
409 multipart_alternative = NULL;
412 if (has_attachments) {
413 CamelMultipart *multipart;
415 multipart = camel_multipart_new ();
416 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
417 camel_multipart_set_boundary (multipart, NULL);
419 part = camel_mime_part_new ();
420 camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (multipart_body));
421 camel_multipart_add_part (multipart, part);
422 g_object_unref (part);
424 e_attachment_store_add_to_multipart (attachment_store, multipart, "UTF-8");
426 g_object_unref (multipart_body);
427 multipart_body = multipart;
430 camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart_body));
432 g_slist_free_full (inline_images_parts, g_object_unref);
433 g_clear_object (&multipart_alternative);
434 g_clear_object (&multipart_body);
435 } else {
436 gchar *text;
438 text = e_content_editor_get_content (
439 cnt_editor,
440 E_CONTENT_EDITOR_GET_TEXT_PLAIN |
441 E_CONTENT_EDITOR_GET_PROCESSED,
442 NULL, NULL);
444 if (has_attachments && !has_text && (!text || !*text)) {
445 /* Text is required, thus if there are attachments,
446 but no text, then store at least a space. */
447 g_free (text);
448 text = g_strdup (" ");
451 if (text && *text) {
452 if (has_attachments) {
453 CamelMultipart *multipart;
454 CamelMimePart *part;
456 multipart = camel_multipart_new ();
457 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
458 camel_multipart_set_boundary (multipart, NULL);
460 part = camel_mime_part_new ();
461 camel_mime_part_set_content (part, text, strlen (text), "text/plain");
462 camel_multipart_add_part (multipart, part);
463 g_object_unref (part);
465 e_attachment_store_add_to_multipart (attachment_store, multipart, "UTF-8");
467 camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart));
469 g_object_unref (multipart);
470 } else {
471 camel_mime_part_set_content (CAMEL_MIME_PART (message), text, strlen (text), "text/plain");
473 has_text = TRUE;
476 g_free (text);
479 if (has_text) {
480 camel_mime_message_encode_8bit_parts (message);
481 } else {
482 g_clear_object (&message);
485 return message;
488 static void
489 e_mail_notes_retrieve_message_thread (EAlertSinkThreadJobData *job_data,
490 gpointer user_data,
491 GCancellable *cancellable,
492 GError **error)
494 EMailNotesEditor *notes_editor = user_data;
495 CamelMimeMessage *message;
497 if (g_cancellable_set_error_if_cancelled (cancellable, error))
498 return;
500 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
502 message = camel_folder_get_message_sync (notes_editor->folder, notes_editor->uid, cancellable, error);
503 if (!g_cancellable_is_cancelled (cancellable))
504 notes_editor->message = message;
505 else
506 g_clear_object (&message);
509 static void
510 e_mail_notes_retrieve_message_done (gpointer ptr)
512 EMailNotesEditor *notes_editor = ptr;
514 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
516 if (notes_editor->message) {
517 EActivityBar *activity_bar;
518 CamelDataWrapper *content;
519 CamelContentType *ct;
521 content = camel_medium_get_content (CAMEL_MEDIUM (notes_editor->message));
522 ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (notes_editor->message));
524 if (ct && camel_content_type_is (ct, "multipart", "mixed") && CAMEL_IS_MULTIPART (content)) {
525 CamelMultipart *multipart = CAMEL_MULTIPART (content);
526 guint nparts, ii;
528 nparts = camel_multipart_get_number (multipart);
529 for (ii = 0; ii < nparts; ii++) {
530 CamelMimePart *part;
531 CamelContentType *ct;
532 const gchar *x_evolution_note;
534 part = camel_multipart_get_part (multipart, ii);
535 if (!part)
536 continue;
538 ct = camel_mime_part_get_content_type (part);
539 if (!ct || !camel_content_type_is (ct, "message", "rfc822"))
540 continue;
542 x_evolution_note = camel_medium_get_header (CAMEL_MEDIUM (part), E_MAIL_NOTES_HEADER);
543 if (x_evolution_note) {
544 content = camel_medium_get_content (CAMEL_MEDIUM (part));
545 if (CAMEL_IS_MIME_MESSAGE (content)) {
546 e_mail_notes_editor_extract_text_from_message (notes_editor,
547 CAMEL_MIME_MESSAGE (content));
549 break;
554 g_clear_object (&notes_editor->message);
555 notes_editor->had_message = TRUE;
557 activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
558 e_activity_bar_set_activity (activity_bar, NULL);
559 } else {
560 GtkAction *action;
562 action = gtk_action_group_get_action (notes_editor->action_group, "save-and-close");
563 gtk_action_set_sensitive (action, FALSE);
566 g_object_unref (notes_editor);
569 static gboolean
570 mail_notes_editor_delete_event_cb (EMailNotesEditor *notes_editor,
571 GdkEvent *event)
573 GtkActionGroup *action_group;
574 GtkAction *action;
576 action_group = notes_editor->action_group;
577 action = gtk_action_group_get_action (action_group, "close");
578 gtk_action_activate (action);
580 return TRUE;
583 static void
584 notes_editor_activity_notify_cb (EActivityBar *activity_bar,
585 GParamSpec *param,
586 EMailNotesEditor *notes_editor)
588 EContentEditor *cnt_editor;
589 GtkAction *action;
590 gboolean can_edit;
592 g_return_if_fail (E_IS_ACTIVITY_BAR (activity_bar));
593 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
595 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
596 can_edit = notes_editor->had_message && !e_activity_bar_get_activity (activity_bar);
598 g_object_set (cnt_editor, "editable", can_edit, NULL);
600 action = gtk_action_group_get_action (notes_editor->action_group, "save-and-close");
601 gtk_action_set_sensitive (action, can_edit);
604 static gboolean
605 e_mail_notes_replace_message_in_folder_sync (CamelFolder *folder,
606 const gchar *uid,
607 CamelMimeMessage *message,
608 gboolean has_note,
609 GCancellable *cancellable,
610 GError **error)
612 CamelMessageInfo *mi;
613 gboolean success = FALSE;
615 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
616 g_return_val_if_fail (uid != NULL, FALSE);
617 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
619 mi = camel_folder_get_message_info (folder, uid);
620 if (mi) {
621 CamelMessageInfo *clone;
622 gchar *appended_uid = NULL;
624 clone = camel_message_info_clone (mi, NULL);
625 camel_message_info_set_abort_notifications (clone, TRUE);
627 camel_message_info_set_user_flag (clone, E_MAIL_NOTES_USER_FLAG, has_note);
629 success = camel_folder_append_message_sync (folder, message, clone,
630 &appended_uid, cancellable, error);
632 if (success)
633 camel_message_info_set_flags (mi, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
635 g_clear_object (&clone);
636 g_clear_object (&mi);
637 g_free (appended_uid);
638 } else {
639 g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot find message in its folder summary"));
642 return success;
645 static gboolean
646 e_mail_notes_replace_note (CamelMimeMessage *message,
647 CamelMimeMessage *note)
649 CamelMultipart *multipart;
650 CamelMimePart *part;
651 CamelDataWrapper *orig_content;
652 CamelContentType *ct;
654 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
655 if (note)
656 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (note), FALSE);
658 orig_content = camel_medium_get_content (CAMEL_MEDIUM (message));
659 ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (message));
660 if (ct && camel_content_type_is (ct, "multipart", "mixed") && CAMEL_IS_MULTIPART (orig_content)) {
661 CamelMimePart *content_adept = NULL;
662 CamelMultipart *multipart = CAMEL_MULTIPART (orig_content);
663 guint nparts, ii;
665 nparts = camel_multipart_get_number (multipart);
666 for (ii = 0; ii < nparts; ii++) {
667 CamelMimePart *part;
668 CamelContentType *ct;
669 const gchar *x_evolution_note;
671 part = camel_multipart_get_part (multipart, ii);
672 if (!part)
673 continue;
675 ct = camel_mime_part_get_content_type (part);
676 if (!ct || !camel_content_type_is (ct, "message", "rfc822")) {
677 if (content_adept) {
678 content_adept = NULL;
679 break;
681 content_adept = part;
682 continue;
685 x_evolution_note = camel_medium_get_header (CAMEL_MEDIUM (part), E_MAIL_NOTES_HEADER);
686 if (x_evolution_note)
687 break;
689 if (content_adept) {
690 content_adept = NULL;
691 break;
693 content_adept = part;
696 if (content_adept)
697 orig_content = camel_medium_get_content (CAMEL_MEDIUM (content_adept));
700 if (!orig_content)
701 return FALSE;
703 g_object_ref (orig_content);
705 if (note) {
706 multipart = camel_multipart_new ();
707 camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
708 camel_multipart_set_boundary (multipart, NULL);
710 part = camel_mime_part_new ();
711 camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (orig_content));
712 camel_multipart_add_part (multipart, part);
713 g_object_unref (part);
715 part = camel_mime_part_new ();
716 /* Value doesn't matter, it's checked for an existence only */
717 camel_medium_add_header (CAMEL_MEDIUM (part), E_MAIL_NOTES_HEADER, "True");
718 camel_mime_part_set_disposition (CAMEL_MIME_PART (part), "inline");
719 camel_mime_part_set_description (CAMEL_MIME_PART (part), _("Message Note"));
720 camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (note));
722 camel_mime_part_set_content_type (part, "message/rfc822");
724 camel_multipart_add_part (multipart, part);
725 g_object_unref (part);
727 camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart));
728 } else {
729 camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (orig_content));
732 g_clear_object (&orig_content);
734 return TRUE;
737 static void
738 action_close_cb (GtkAction *action,
739 EMailNotesEditor *notes_editor)
741 EContentEditor *cnt_editor;
742 gboolean something_changed = FALSE;
744 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
746 something_changed = e_content_editor_get_changed (cnt_editor);
748 if (something_changed) {
749 gint response;
751 response = e_alert_run_dialog_for_args (
752 GTK_WINDOW (notes_editor),
753 "mail:ask-mail-note-changed", NULL);
754 if (response == GTK_RESPONSE_YES) {
755 GtkActionGroup *action_group;
757 action_group = notes_editor->action_group;
758 action = gtk_action_group_get_action (
759 action_group, "save-and-close");
760 gtk_action_activate (action);
761 return;
762 } else if (response == GTK_RESPONSE_CANCEL)
763 return;
766 gtk_widget_destroy (GTK_WIDGET (notes_editor));
769 typedef struct {
770 EMailNotesEditor *notes_editor;
771 CamelMimeMessage *inner_message;
772 gboolean success;
773 } SaveAndCloseData;
775 static void
776 save_and_close_data_free (gpointer ptr)
778 SaveAndCloseData *scd = ptr;
780 if (scd) {
781 if (scd->success)
782 gtk_widget_destroy (GTK_WIDGET (scd->notes_editor));
783 else
784 g_clear_object (&scd->notes_editor);
785 g_clear_object (&scd->inner_message);
786 g_free (scd);
790 static void
791 e_mail_notes_store_changes_thread (EAlertSinkThreadJobData *job_data,
792 gpointer user_data,
793 GCancellable *cancellable,
794 GError **error)
796 CamelMimeMessage *message;
797 SaveAndCloseData *scd = user_data;
799 g_return_if_fail (scd != NULL);
801 if (g_cancellable_set_error_if_cancelled (cancellable, error))
802 return;
804 if (!scd->inner_message) {
805 scd->success = e_mail_notes_remove_sync (scd->notes_editor->folder,
806 scd->notes_editor->uid, cancellable, error);
807 return;
810 message = camel_folder_get_message_sync (scd->notes_editor->folder, scd->notes_editor->uid, cancellable, error);
811 if (!message)
812 return;
814 e_mail_notes_replace_note (message, scd->inner_message);
816 scd->success = e_mail_notes_replace_message_in_folder_sync (scd->notes_editor->folder,
817 scd->notes_editor->uid, message, TRUE, cancellable, error);
819 g_clear_object (&message);
822 static void
823 action_save_and_close_cb (GtkAction *action,
824 EMailNotesEditor *notes_editor)
826 SaveAndCloseData *scd;
827 gchar *full_display_name;
828 EActivityBar *activity_bar;
829 EActivity *activity;
831 g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
833 scd = g_new0 (SaveAndCloseData, 1);
834 scd->notes_editor = g_object_ref (notes_editor);
835 scd->inner_message = e_mail_notes_editor_encode_text_to_message (notes_editor);
836 scd->success = FALSE;
838 full_display_name = e_mail_folder_to_full_display_name (notes_editor->folder, NULL);
840 activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
841 activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
842 _("Storing changes..."), "mail:failed-store-note",
843 full_display_name ? full_display_name : camel_folder_get_display_name (notes_editor->folder),
844 e_mail_notes_store_changes_thread,
845 scd, save_and_close_data_free);
846 e_activity_bar_set_activity (activity_bar, activity);
847 g_clear_object (&activity);
849 g_free (full_display_name);
852 static void
853 e_mail_notes_editor_dispose (GObject *object)
855 EMailNotesEditor *notes_editor = E_MAIL_NOTES_EDITOR (object);
857 if (notes_editor->editor) {
858 EActivityBar *activity_bar;
860 activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
861 g_signal_handlers_disconnect_by_func (activity_bar,
862 G_CALLBACK (notes_editor_activity_notify_cb), notes_editor);
864 notes_editor->editor = NULL;
867 g_clear_object (&notes_editor->focus_tracker);
868 g_clear_object (&notes_editor->action_group);
870 /* Chain up to parent's method */
871 G_OBJECT_CLASS (e_mail_notes_editor_parent_class)->dispose (object);
874 static void
875 e_mail_notes_editor_finalize (GObject *object)
877 EMailNotesEditor *notes_editor = E_MAIL_NOTES_EDITOR (object);
879 g_clear_object (&notes_editor->focus_tracker);
880 g_clear_object (&notes_editor->folder);
881 g_clear_object (&notes_editor->message);
882 g_free (notes_editor->uid);
884 /* Chain up to parent's method */
885 G_OBJECT_CLASS (e_mail_notes_editor_parent_class)->finalize (object);
888 static void
889 e_mail_notes_editor_class_init (EMailNotesEditorClass *klass)
891 GObjectClass *object_class;
893 object_class = G_OBJECT_CLASS (klass);
894 object_class->dispose = e_mail_notes_editor_dispose;
895 object_class->finalize = e_mail_notes_editor_finalize;
898 static void
899 e_mail_notes_editor_init (EMailNotesEditor *notes_editor)
903 static EMailNotesEditor *
904 e_mail_notes_editor_new_with_editor (EHTMLEditor *html_editor,
905 GtkWindow *parent,
906 CamelFolder *folder,
907 const gchar *uid)
909 const gchar *ui =
910 "<ui>\n"
911 " <menubar name='main-menu'>\n"
912 " <placeholder name='pre-edit-menu'>\n"
913 " <menu action='file-menu'>\n"
914 " <menuitem action='save-and-close'/>\n"
915 " <separator/>"
916 " <menuitem action='close'/>\n"
917 " </menu>\n"
918 " </placeholder>\n"
919 " </menubar>\n"
920 " <toolbar name='main-toolbar'>\n"
921 " <placeholder name='pre-main-toolbar'>\n"
922 " <toolitem action='save-and-close'/>\n"
923 " </placeholder>\n"
924 " </toolbar>\n"
925 "</ui>";
927 GtkActionEntry entries[] = {
929 { "close",
930 "window-close",
931 N_("_Close"),
932 "<Control>w",
933 N_("Close"),
934 G_CALLBACK (action_close_cb) },
936 { "save-and-close",
937 "document-save",
938 N_("_Save and Close"),
939 "<Control>Return",
940 N_("Save and Close"),
941 G_CALLBACK (action_save_and_close_cb) },
943 { "file-menu",
944 NULL,
945 N_("_File"),
946 NULL,
947 NULL,
948 NULL }
951 EMailNotesEditor *notes_editor;
952 EContentEditor *cnt_editor;
953 EFocusTracker *focus_tracker;
954 EActivityBar *activity_bar;
955 GtkUIManager *ui_manager;
956 GtkWidget *widget, *content;
957 GtkActionGroup *action_group;
958 GtkAction *action;
959 GSettings *settings;
960 GError *local_error = NULL;
962 notes_editor = g_object_new (E_TYPE_MAIL_NOTES_EDITOR, NULL);
964 g_object_set (G_OBJECT (notes_editor),
965 "transient-for", parent,
966 "destroy-with-parent", TRUE,
967 "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
968 "title", _("Edit Message Note"),
969 NULL);
971 gtk_window_set_default_size (GTK_WINDOW (notes_editor), 600, 440);
973 widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
974 gtk_container_add (GTK_CONTAINER (notes_editor), widget);
975 gtk_widget_show (widget);
977 content = widget;
979 widget = GTK_WIDGET (html_editor);
981 notes_editor->editor = html_editor;
982 cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
983 ui_manager = e_html_editor_get_ui_manager (notes_editor->editor);
985 /* Because we are loading from a hard-coded string, there is
986 * no chance of I/O errors. Failure here implies a malformed
987 * UI definition. Full stop. */
988 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &local_error);
989 if (local_error != NULL)
990 g_error ("%s: Failed to load built-in UI definition: %s", G_STRFUNC, local_error->message);
992 action_group = gtk_action_group_new ("notes");
993 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
994 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), notes_editor);
995 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
996 notes_editor->action_group = g_object_ref (action_group);
998 /* Hide page properties because it is not inherited in the mail. */
999 action = e_html_editor_get_action (notes_editor->editor, "properties-page");
1000 gtk_action_set_visible (action, FALSE);
1002 action = e_html_editor_get_action (notes_editor->editor, "context-properties-page");
1003 gtk_action_set_visible (action, FALSE);
1005 gtk_ui_manager_ensure_update (ui_manager);
1007 /* Construct the window content. */
1009 widget = e_html_editor_get_managed_widget (notes_editor->editor, "/main-menu");
1010 gtk_box_pack_start (GTK_BOX (content), widget, FALSE, FALSE, 0);
1011 gtk_widget_show (widget);
1013 widget = e_html_editor_get_managed_widget (notes_editor->editor, "/main-toolbar");
1014 gtk_box_pack_start (GTK_BOX (content), widget, FALSE, FALSE, 0);
1015 gtk_widget_show (widget);
1017 widget = GTK_WIDGET (notes_editor->editor);
1018 g_object_set (G_OBJECT (widget),
1019 "halign", GTK_ALIGN_FILL,
1020 "hexpand", TRUE,
1021 "valign", GTK_ALIGN_FILL,
1022 "vexpand", TRUE,
1023 NULL);
1024 gtk_box_pack_start (GTK_BOX (content), widget, TRUE, TRUE, 0);
1025 gtk_widget_show (widget);
1027 widget = e_attachment_paned_new ();
1028 gtk_box_pack_start (GTK_BOX (content), widget, FALSE, FALSE, 0);
1029 notes_editor->attachment_paned = E_ATTACHMENT_PANED (widget);
1030 gtk_widget_show (widget);
1032 e_binding_bind_property (
1033 cnt_editor, "editable",
1034 widget, "sensitive",
1035 G_BINDING_SYNC_CREATE);
1037 /* Configure an EFocusTracker to manage selection actions. */
1038 focus_tracker = e_focus_tracker_new (GTK_WINDOW (notes_editor));
1040 action = e_html_editor_get_action (notes_editor->editor, "cut");
1041 e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
1043 action = e_html_editor_get_action (notes_editor->editor, "copy");
1044 e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
1046 action = e_html_editor_get_action (notes_editor->editor, "paste");
1047 e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
1049 action = e_html_editor_get_action (notes_editor->editor, "select-all");
1050 e_focus_tracker_set_select_all_action (focus_tracker, action);
1052 notes_editor->focus_tracker = focus_tracker;
1054 gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
1056 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1057 e_content_editor_set_html_mode (
1058 cnt_editor, g_settings_get_boolean (settings, "composer-send-html"));
1059 g_object_unref (settings);
1061 g_signal_connect (
1062 notes_editor, "delete-event",
1063 G_CALLBACK (mail_notes_editor_delete_event_cb), NULL);
1065 activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
1067 g_signal_connect (activity_bar, "notify::activity",
1068 G_CALLBACK (notes_editor_activity_notify_cb), notes_editor);
1070 notes_editor->folder = g_object_ref (folder);
1071 notes_editor->uid = g_strdup (uid);
1072 notes_editor->had_message = FALSE;
1074 return notes_editor;
1077 typedef struct _AsyncData {
1078 GtkWindow *parent;
1079 CamelFolder *folder;
1080 gchar *uid;
1081 } AsyncData;
1083 static void
1084 async_data_free (gpointer ptr)
1086 AsyncData *ad = ptr;
1088 if (ad) {
1089 g_clear_object (&ad->parent);
1090 g_clear_object (&ad->folder);
1091 g_free (ad->uid);
1092 g_free (ad);
1096 static void
1097 e_mail_notes_editor_ready_cb (GObject *source_object,
1098 GAsyncResult *result,
1099 gpointer user_data)
1101 AsyncData *ad = user_data;
1102 GtkWidget *html_editor;
1103 GError *error = NULL;
1105 g_return_if_fail (result != NULL);
1106 g_return_if_fail (ad != NULL);
1108 html_editor = e_html_editor_new_finish (result, &error);
1109 if (error) {
1110 g_warning ("%s: Failed to create HTML editor: %s", G_STRFUNC, error->message);
1111 g_clear_error (&error);
1112 } else {
1113 EMailNotesEditor *notes_editor;
1114 EActivityBar *activity_bar;
1115 EActivity *activity;
1117 notes_editor = e_mail_notes_editor_new_with_editor (E_HTML_EDITOR (html_editor),
1118 ad->parent, ad->folder, ad->uid);
1120 activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
1121 activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
1122 _("Retrieving message..."), "mail:no-retrieve-message", NULL,
1123 e_mail_notes_retrieve_message_thread,
1124 g_object_ref (notes_editor), e_mail_notes_retrieve_message_done);
1125 e_activity_bar_set_activity (activity_bar, activity);
1126 g_clear_object (&activity);
1128 gtk_widget_show (GTK_WIDGET (notes_editor));
1131 async_data_free (ad);
1134 void
1135 e_mail_notes_edit (GtkWindow *parent,
1136 CamelFolder *folder,
1137 const gchar *uid)
1139 AsyncData *ad;
1141 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1142 g_return_if_fail (uid != NULL);
1144 ad = g_new0 (AsyncData, 1);
1145 ad->parent = parent ? g_object_ref (parent) : NULL;
1146 ad->folder = g_object_ref (folder);
1147 ad->uid = g_strdup (uid);
1149 e_html_editor_new (e_mail_notes_editor_ready_cb, ad);
1152 gboolean
1153 e_mail_notes_remove_sync (CamelFolder *folder,
1154 const gchar *uid,
1155 GCancellable *cancellable,
1156 GError **error)
1158 CamelMimeMessage *message;
1159 gboolean success;
1161 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1162 g_return_val_if_fail (uid != NULL, FALSE);
1164 message = camel_folder_get_message_sync (folder, uid, cancellable, error);
1165 if (!message)
1166 return FALSE;
1168 success = e_mail_notes_replace_note (message, NULL);
1169 if (success) {
1170 success = e_mail_notes_replace_message_in_folder_sync (folder,
1171 uid, message, FALSE, cancellable, error);
1172 } else {
1173 /* There was no note found in the message, thus it was successfully removed */
1174 success = TRUE;
1177 g_clear_object (&message);
1179 return success;