1 /*
2  *
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful, but
8  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10  * for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program; if not, see <http://www.gnu.org/licenses/>.
14  *
15  *
16  * Authors:
17  *		Michael Zucchi <notzed@novell.com>
18  *		Rodrigo Moya <rodrigo@novell.com>
19  *
20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21  *
22  */
23 
24 /* Convert a mail message into a task */
25 
26 #include "evolution-config.h"
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <glib/gi18n-lib.h>
31 
32 #include <libecal/libecal.h>
33 
34 #include <shell/e-shell-view.h>
35 #include <shell/e-shell-window-actions.h>
36 
37 #include <mail/e-mail-browser.h>
38 #include <mail/em-utils.h>
39 #include <mail/message-list.h>
40 
41 #include <calendar/gui/e-comp-editor.h>
42 #include <calendar/gui/itip-utils.h>
43 
44 #include "libemail-engine/libemail-engine.h"
45 
46 #define E_SHELL_WINDOW_ACTION_CONVERT_TO_APPOINTMENT(window) \
47 	E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-appointment")
48 #define E_SHELL_WINDOW_ACTION_CONVERT_TO_MEETING(window) \
49 	E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-meeting")
50 #define E_SHELL_WINDOW_ACTION_CONVERT_TO_MEMO(window) \
51 	E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-memo")
52 #define E_SHELL_WINDOW_ACTION_CONVERT_TO_TASK(window) \
53 	E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-task")
54 
55 gboolean	mail_browser_init		(GtkUIManager *ui_manager,
56 						 EMailBrowser *browser);
57 gboolean	mail_shell_view_init		(GtkUIManager *ui_manager,
58 						 EShellView *shell_view);
59 
60 static ECompEditor *
get_component_editor(EShell * shell,ECalClient * client,ECalComponent * comp,gboolean is_new,GError ** error)61 get_component_editor (EShell *shell,
62                       ECalClient *client,
63                       ECalComponent *comp,
64                       gboolean is_new,
65                       GError **error)
66 {
67 	ECompEditorFlags flags = 0;
68 	ECompEditor *comp_editor = NULL;
69 	ESourceRegistry *registry;
70 
71 	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
72 	g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
73 	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
74 
75 	registry = e_shell_get_registry (shell);
76 
77 	if (is_new) {
78 		flags |= E_COMP_EDITOR_FLAG_IS_NEW;
79 	} else {
80 		comp_editor = e_comp_editor_find_existing_for (
81 			e_client_get_source (E_CLIENT (client)),
82 			e_cal_component_get_icalcomponent (comp));
83 	}
84 
85 	if (!comp_editor) {
86 		if (itip_organizer_is_user (registry, comp, client))
87 			flags |= E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
88 		if (e_cal_component_has_attendees (comp))
89 			flags |= E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
90 
91 		comp_editor = e_comp_editor_open_for_component (NULL,
92 			shell, e_client_get_source (E_CLIENT (client)),
93 			e_cal_component_get_icalcomponent (comp), flags);
94 
95 		if (comp_editor) {
96 			/* request save for new events */
97 			e_comp_editor_set_changed (comp_editor, is_new);
98 		}
99 	}
100 
101 	return comp_editor;
102 }
103 
104 static void
set_attendees(ECalComponent * comp,CamelMimeMessage * message,const gchar * organizer)105 set_attendees (ECalComponent *comp,
106                CamelMimeMessage *message,
107                const gchar *organizer)
108 {
109 	GSList *attendees = NULL;
110 	ECalComponentAttendee *ca;
111 	CamelInternetAddress *from, *to, *cc, *bcc, *arr[4];
112 	gint len, i, j;
113 
114 	from = camel_mime_message_get_reply_to (message);
115 	if (!from)
116 		from = camel_mime_message_get_from (message);
117 
118 	to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
119 	cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
120 	bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
121 
122 	arr[0] = from; arr[1] = to; arr[2] = cc; arr[3] = bcc;
123 
124 	for (j = 0; j < 4; j++) {
125 		if (!arr[j])
126 			continue;
127 
128 		len = camel_address_length (CAMEL_ADDRESS (arr[j]));
129 		for (i = 0; i < len; i++) {
130 			const gchar *name, *addr;
131 
132 			if (camel_internet_address_get (arr[j], i, &name, &addr)) {
133 				gchar *temp;
134 
135 				temp = g_strconcat ("mailto:", addr, NULL);
136 				if (organizer && g_ascii_strcasecmp (temp, organizer) == 0) {
137 					/* do not add organizer twice */
138 					g_free (temp);
139 					continue;
140 				}
141 
142 				ca = e_cal_component_attendee_new ();
143 
144 				e_cal_component_attendee_set_value (ca, temp);
145 				e_cal_component_attendee_set_cn (ca, name);
146 				e_cal_component_attendee_set_cutype (ca, I_CAL_CUTYPE_INDIVIDUAL);
147 				e_cal_component_attendee_set_partstat (ca, I_CAL_PARTSTAT_NEEDSACTION);
148 				if (j == 0) {
149 					/* From */
150 					e_cal_component_attendee_set_role (ca, I_CAL_ROLE_CHAIR);
151 				} else if (j == 2) {
152 					/* BCC  */
153 					e_cal_component_attendee_set_role (ca, I_CAL_ROLE_OPTPARTICIPANT);
154 				} else {
155 					/* all other */
156 					e_cal_component_attendee_set_role (ca, I_CAL_ROLE_REQPARTICIPANT);
157 				}
158 
159 				attendees = g_slist_prepend (attendees, ca);
160 
161 				g_free (temp);
162 			}
163 		}
164 	}
165 
166 	attendees = g_slist_reverse (attendees);
167 
168 	e_cal_component_set_attendees (comp, attendees);
169 
170 	g_slist_free_full (attendees, e_cal_component_attendee_free);
171 }
172 
173 static const gchar *
prepend_from(CamelMimeMessage * message,gchar ** text)174 prepend_from (CamelMimeMessage *message,
175               gchar **text)
176 {
177 	gchar *res, *tmp, *addr = NULL;
178 	const gchar *name = NULL, *eml = NULL;
179 	CamelInternetAddress *from;
180 
181 	g_return_val_if_fail (message != NULL, NULL);
182 	g_return_val_if_fail (text != NULL, NULL);
183 
184 	from = camel_mime_message_get_reply_to (message);
185 	if (!from)
186 		from = camel_mime_message_get_from (message);
187 
188 	if (from && camel_internet_address_get (from, 0, &name, &eml))
189 		addr = camel_internet_address_format_address (name, eml);
190 
191 	if (addr && !g_utf8_validate (addr, -1, NULL)) {
192 		tmp = e_util_utf8_make_valid (addr);
193 		g_free (addr);
194 		addr = tmp;
195 	}
196 
197 	/* To Translators: The full sentence looks like: "Created from a mail by John Doe <john.doe@myco.example>" */
198 	tmp = g_strdup_printf (_("Created from a mail by %s"), addr ? addr : "");
199 
200 	res = g_strconcat (tmp, "\n", *text, NULL);
201 
202 	g_free (tmp);
203 	g_free (addr);
204 	g_free (*text);
205 
206 	*text = res;
207 
208 	return res;
209 }
210 
211 static void
set_description(ECalComponent * comp,CamelMimeMessage * message,const gchar * default_charset,const gchar * forced_charset)212 set_description (ECalComponent *comp,
213 		 CamelMimeMessage *message,
214 		 const gchar *default_charset,
215 		 const gchar *forced_charset)
216 {
217 	CamelDataWrapper *content;
218 	CamelStream *stream;
219 	CamelContentType *type;
220 	CamelMimePart *mime_part = CAMEL_MIME_PART (message);
221 	const gchar *charset = NULL;
222 	ECalComponentText *text = NULL;
223 	GByteArray *byte_array;
224 	GSList *sl = NULL;
225 	gchar *str, *convert_str = NULL;
226 	gint count = 2;
227 
228 	content = camel_medium_get_content ((CamelMedium *) message);
229 	if (!content)
230 		return;
231 
232 	/*
233 	 * Get non-multipart content from multipart message.
234 	 */
235 	while (CAMEL_IS_MULTIPART (content) && count > 0) {
236 		mime_part = camel_multipart_get_part (CAMEL_MULTIPART (content), 0);
237 		content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
238 		count--;
239 	}
240 
241 	if (!mime_part)
242 		return;
243 
244 	type = camel_mime_part_get_content_type (mime_part);
245 	if (!camel_content_type_is (type, "text", "plain"))
246 		return;
247 
248 	byte_array = g_byte_array_new ();
249 	stream = camel_stream_mem_new_with_byte_array (byte_array);
250 	camel_data_wrapper_decode_to_stream_sync (content, stream, NULL, NULL);
251 	str = g_strndup ((gchar *) byte_array->data, byte_array->len);
252 	g_object_unref (stream);
253 
254 	if (forced_charset && *forced_charset) {
255 		charset = forced_charset;
256 	} else {
257 		CamelContentType *mime_type;
258 
259 		mime_type = camel_data_wrapper_get_mime_type_field (content);
260 
261 		if (mime_type) {
262 			charset = camel_content_type_param (mime_type, "charset");
263 			if (charset && !*charset)
264 				charset = NULL;
265 		}
266 	}
267 
268 	if (!charset && default_charset && *default_charset)
269 		charset = default_charset;
270 
271 	/* convert to UTF-8 string */
272 	if (str && charset) {
273 		gsize bytes_read, bytes_written;
274 
275 		convert_str = g_convert (
276 			str, strlen (str),
277 			"UTF-8", charset,
278 			&bytes_read, &bytes_written, NULL);
279 	}
280 
281 	if (!convert_str && str)
282 		convert_str = e_util_utf8_make_valid (str);
283 
284 	if (convert_str)
285 		text = e_cal_component_text_new (prepend_from (message, &convert_str), NULL);
286 	else
287 		text = e_cal_component_text_new (prepend_from (message, &str), NULL);
288 	sl = g_slist_append (sl, text);
289 
290 	e_cal_component_set_descriptions (comp, sl);
291 
292 	g_free (str);
293 	g_free (convert_str);
294 	g_slist_free_full (sl, e_cal_component_text_free);
295 }
296 
297 static gchar *
set_organizer(ECalComponent * comp,CamelMimeMessage * message,CamelFolder * folder,const gchar * message_uid)298 set_organizer (ECalComponent *comp,
299 	       CamelMimeMessage *message,
300 	       CamelFolder *folder,
301 	       const gchar *message_uid)
302 {
303 	EShell *shell;
304 	ESource *source = NULL;
305 	ESourceRegistry *registry;
306 	ESourceMailIdentity *extension;
307 	const gchar *extension_name;
308 	const gchar *address, *name;
309 	gchar *mailto = NULL;
310 	gchar *identity_name = NULL, *identity_address = NULL;
311 
312 	shell = e_shell_get_default ();
313 	registry = e_shell_get_registry (shell);
314 
315 	source = em_utils_guess_mail_identity_with_recipients (registry, message, folder,
316 		message_uid, &identity_name, &identity_address);
317 
318 	if (!source && folder) {
319 		CamelStore *store;
320 
321 		store = camel_folder_get_parent_store (folder);
322 		source = em_utils_ref_mail_identity_for_store (registry, store);
323 	}
324 
325 	if (source == NULL)
326 		source = e_source_registry_ref_default_mail_identity (registry);
327 
328 	g_return_val_if_fail (source != NULL, NULL);
329 
330 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
331 	extension = e_source_get_extension (source, extension_name);
332 
333 	name = identity_name;
334 	if (!name || !*name)
335 		name = e_source_mail_identity_get_name (extension);
336 
337 	address = identity_address;
338 	if (!address || !*address) {
339 		name = e_source_mail_identity_get_name (extension);
340 		address = e_source_mail_identity_get_address (extension);
341 	}
342 
343 	if (address && *address) {
344 		ECalComponentOrganizer *organizer;
345 
346 		mailto = g_strconcat ("mailto:", address, NULL);
347 
348 		organizer = e_cal_component_organizer_new ();
349 		e_cal_component_organizer_set_value (organizer, mailto);
350 		e_cal_component_organizer_set_cn (organizer, name);
351 		e_cal_component_set_organizer (comp, organizer);
352 		e_cal_component_organizer_free (organizer);
353 	}
354 
355 	g_object_unref (source);
356 	g_free (identity_name);
357 	g_free (identity_address);
358 
359 	return mailto;
360 }
361 
362 struct _att_async_cb_data {
363 	gchar **uris;
364 	EFlag *flag;
365 };
366 
367 static void
attachment_load_finished(EAttachmentStore * store,GAsyncResult * result,gpointer user_data)368 attachment_load_finished (EAttachmentStore *store,
369                           GAsyncResult *result,
370                           gpointer user_data)
371 {
372 	struct _att_async_cb_data *data = user_data;
373 
374 	/* XXX Should be no need to check for error here.
375 	 *     This is just to reset state in the EAttachment. */
376 	e_attachment_store_load_finish (store, result, NULL);
377 
378 	e_flag_set (data->flag);
379 }
380 
381 static void
attachment_save_finished(EAttachmentStore * store,GAsyncResult * result,gpointer user_data)382 attachment_save_finished (EAttachmentStore *store,
383                           GAsyncResult *result,
384                           gpointer user_data)
385 {
386 	struct _att_async_cb_data *data = user_data;
387 	gchar **uris;
388 	GError *error = NULL;
389 
390 	uris = e_attachment_store_save_finish (store, result, &error);
391 	if (error != NULL)
392 		data->uris = NULL;
393 	else
394 		data->uris = uris;
395 
396 	g_clear_error (&error);
397 
398 	e_flag_set (data->flag);
399 }
400 
401 static void
set_attachments(ECalClient * client,ECalComponent * comp,CamelMimeMessage * message)402 set_attachments (ECalClient *client,
403                  ECalComponent *comp,
404                  CamelMimeMessage *message)
405 {
406 	/* XXX Much of this is copied from CompEditor::get_attachment_list().
407 	 *     Perhaps it should be split off as a separate utility? */
408 
409 	EAttachmentStore *store;
410 	CamelDataWrapper *content;
411 	CamelMultipart *multipart;
412 	GFile *destination;
413 	GList *attachment_list = NULL;
414 	GSList *uri_list = NULL;
415 	const gchar *comp_uid = NULL;
416 	const gchar *local_store;
417 	gchar *filename_prefix, *tmp;
418 	gint ii, n_parts;
419 	struct _att_async_cb_data cb_data;
420 
421 	cb_data.flag = e_flag_new ();
422 	cb_data.uris = NULL;
423 
424 	content = camel_medium_get_content ((CamelMedium *) message);
425 	if (!content || !CAMEL_IS_MULTIPART (content))
426 		return;
427 
428 	n_parts = camel_multipart_get_number (CAMEL_MULTIPART (content));
429 	if (n_parts < 1)
430 		return;
431 
432 	comp_uid = e_cal_component_get_uid (comp);
433 	g_return_if_fail (comp_uid != NULL);
434 
435 	tmp = g_strdup (comp_uid);
436 	e_util_make_safe_filename (tmp);
437 	filename_prefix = g_strconcat (tmp, "-", NULL);
438 	g_free (tmp);
439 
440 	local_store = e_cal_client_get_local_attachment_store (client);
441 	destination = g_file_new_for_path (local_store);
442 
443 	/* Create EAttachments from the MIME parts and add them to the
444 	 * attachment store. */
445 
446 	multipart = CAMEL_MULTIPART (content);
447 	store = E_ATTACHMENT_STORE (e_attachment_store_new ());
448 
449 	for (ii = 1; ii < n_parts; ii++) {
450 		EAttachment *attachment;
451 		CamelMimePart *mime_part;
452 
453 		attachment = e_attachment_new ();
454 		mime_part = camel_multipart_get_part (multipart, ii);
455 		e_attachment_set_mime_part (attachment, mime_part);
456 
457 		attachment_list = g_list_append (attachment_list, attachment);
458 	}
459 
460 	e_flag_clear (cb_data.flag);
461 
462 	e_attachment_store_load_async (
463 		store, attachment_list, (GAsyncReadyCallback)
464 		attachment_load_finished, &cb_data);
465 
466 	/* Loading should be instantaneous since we already have
467 	 * the full content, but we need to wait for the callback.
468 	 */
469 	e_flag_wait (cb_data.flag);
470 
471 	g_list_foreach (attachment_list, (GFunc) g_object_unref, NULL);
472 	g_list_free (attachment_list);
473 
474 	cb_data.uris = NULL;
475 	e_flag_clear (cb_data.flag);
476 
477 	e_attachment_store_save_async (
478 		store, destination, filename_prefix,
479 		(GAsyncReadyCallback) attachment_save_finished, &cb_data);
480 
481 	g_free (filename_prefix);
482 
483 	/* We can't return until we have results. */
484 	e_flag_wait (cb_data.flag);
485 
486 	if (cb_data.uris == NULL) {
487 		e_flag_free (cb_data.flag);
488 		g_warning ("No attachment URIs retrieved.");
489 		return;
490 	}
491 
492 	/* Transfer the URI strings to the GSList. */
493 	for (ii = 0; cb_data.uris[ii] != NULL; ii++) {
494 		uri_list = g_slist_prepend (uri_list, i_cal_attach_new_from_url (cb_data.uris[ii]));
495 	}
496 
497 	e_flag_free (cb_data.flag);
498 	g_strfreev (cb_data.uris);
499 
500 	e_cal_component_set_attachments (comp, uri_list);
501 
502 	g_slist_free_full (uri_list, g_object_unref);
503 	e_attachment_store_remove_all (store);
504 	g_object_unref (destination);
505 	g_object_unref (store);
506 }
507 
508 static void
set_priority(ECalComponent * comp,CamelMimePart * part)509 set_priority (ECalComponent *comp,
510               CamelMimePart *part)
511 {
512 	const gchar *prio;
513 
514 	g_return_if_fail (comp != NULL);
515 	g_return_if_fail (part != NULL);
516 
517 	prio = camel_medium_get_header (CAMEL_MEDIUM (part), "X-Priority");
518 	if (prio && atoi (prio) > 0)
519 		e_cal_component_set_priority (comp, 1);
520 }
521 
522 struct _report_error
523 {
524 	gchar *format;
525 	gchar *param;
526 };
527 
528 static gboolean
do_report_error(struct _report_error * err)529 do_report_error (struct _report_error *err)
530 {
531 	if (err) {
532 		e_notice (NULL, GTK_MESSAGE_ERROR, err->format, err->param);
533 		g_free (err->format);
534 		g_free (err->param);
535 		g_slice_free (struct _report_error, err);
536 	}
537 
538 	return FALSE;
539 }
540 
541 static void
report_error_idle(const gchar * format,const gchar * param)542 report_error_idle (const gchar *format,
543                    const gchar *param)
544 {
545 	struct _report_error *err = g_slice_new (struct _report_error);
546 
547 	err->format = g_strdup (format);
548 	err->param = g_strdup (param);
549 
550 	g_usleep (250);
551 	g_idle_add ((GSourceFunc) do_report_error, err);
552 }
553 
554 struct _manage_comp
555 {
556 	ECalClient *client;
557 	ECalComponent *comp;
558 	ICalComponent *stored_comp; /* the one in client already */
559 	GCond cond;
560 	GMutex mutex;
561 	gint mails_count;
562 	gint mails_done;
563 	gchar *editor_title;
564 	gboolean can_continue;
565 };
566 
567 static void
free_manage_comp_struct(struct _manage_comp * mc)568 free_manage_comp_struct (struct _manage_comp *mc)
569 {
570 	g_return_if_fail (mc != NULL);
571 
572 	g_object_unref (mc->comp);
573 	g_object_unref (mc->client);
574 	g_clear_object (&mc->stored_comp);
575 	g_mutex_clear (&mc->mutex);
576 	g_cond_clear (&mc->cond);
577 	g_free (mc->editor_title);
578 
579 	g_slice_free (struct _manage_comp, mc);
580 }
581 
582 static gint
do_ask(const gchar * text,gboolean is_create_edit_add)583 do_ask (const gchar *text,
584         gboolean is_create_edit_add)
585 {
586 	gint res;
587 	GtkWidget *dialog = gtk_message_dialog_new (
588 		NULL,
589 		GTK_DIALOG_MODAL,
590 		GTK_MESSAGE_QUESTION,
591 		is_create_edit_add ? GTK_BUTTONS_NONE : GTK_BUTTONS_YES_NO,
592 		"%s", text);
593 
594 	if (is_create_edit_add) {
595 		gtk_dialog_add_buttons (
596 			GTK_DIALOG (dialog),
597 			/* Translators: Dialog button to Cancel edit of an existing event/memo/task */
598 			C_("mail-to-task", "_Cancel"), GTK_RESPONSE_CANCEL,
599 			/* Translators: Dialog button to Edit an existing event/memo/task */
600 			C_("mail-to-task", "_Edit"), GTK_RESPONSE_YES,
601 			/* Translators: Dialog button to create a New event/memo/task */
602 			C_("mail-to-task", "_New"), GTK_RESPONSE_NO,
603 			NULL);
604 	}
605 
606 	res = gtk_dialog_run (GTK_DIALOG (dialog));
607 
608 	gtk_widget_destroy (dialog);
609 
610 	return res;
611 }
612 
613 static const gchar *
get_question_edit_old(ECalClientSourceType source_type)614 get_question_edit_old (ECalClientSourceType source_type)
615 {
616 	const gchar *ask = NULL;
617 
618 	switch (source_type) {
619 	case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
620 		ask = _("Selected calendar contains event “%s” already. Would you like to edit the old event?");
621 		break;
622 	case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
623 		ask = _("Selected task list contains task “%s” already. Would you like to edit the old task?");
624 		break;
625 	case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
626 		ask = _("Selected memo list contains memo “%s” already. Would you like to edit the old memo?");
627 		break;
628 	default:
629 		g_warn_if_reached ();
630 		break;
631 	}
632 
633 	return ask;
634 }
635 
636 static const gchar *
get_question_add_all_mails(ECalClientSourceType source_type,gint count)637 get_question_add_all_mails (ECalClientSourceType source_type,
638                             gint count)
639 {
640 	const gchar *ask = NULL;
641 
642 	switch (source_type) {
643 	case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
644 		ask = ngettext (
645 			/* Translators: Note there are always more than 10 mails selected */
646 			"You have selected %d mails to be converted to events. Do you really want to add them all?",
647 			"You have selected %d mails to be converted to events. Do you really want to add them all?",
648 			count);
649 		break;
650 	case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
651 		ask = ngettext (
652 			/* Translators: Note there are always more than 10 mails selected */
653 			"You have selected %d mails to be converted to tasks. Do you really want to add them all?",
654 			"You have selected %d mails to be converted to tasks. Do you really want to add them all?",
655 			count);
656 		break;
657 	case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
658 		ask = ngettext (
659 			/* Translators: Note there are always more than 10 mails selected */
660 			"You have selected %d mails to be converted to memos. Do you really want to add them all?",
661 			"You have selected %d mails to be converted to memos. Do you really want to add them all?",
662 			count);
663 		break;
664 	default:
665 		g_warn_if_reached ();
666 		break;
667 	}
668 
669 	return ask;
670 }
671 
672 static void
comp_editor_closed(ECompEditor * comp_editor,gboolean saved,struct _manage_comp * mc)673 comp_editor_closed (ECompEditor *comp_editor,
674                     gboolean saved,
675                     struct _manage_comp *mc)
676 {
677 	if (!mc)
678 		return;
679 
680 	if (!saved && mc->mails_done < mc->mails_count)
681 		mc->can_continue = (do_ask (_("Do you wish to continue converting remaining mails?"), FALSE) == GTK_RESPONSE_YES);
682 
683 	/* Signal the do_mail_to_event thread that editor was closed and editor
684 	 * for next event can be displayed (if any) */
685 	g_cond_signal (&mc->cond);
686 }
687 
688 /*
689  * This handler takes title of the editor window and
690  * inserts information about number of processed mails and
691  * number of all mails to process, so the window title
692  * will look like "Appointment (3/10) — An appoitment name"
693  */
694 static void
comp_editor_title_changed(GtkWidget * widget,GParamSpec * pspec,struct _manage_comp * mc)695 comp_editor_title_changed (GtkWidget *widget,
696                            GParamSpec *pspec,
697                            struct _manage_comp *mc)
698 {
699 	GtkWindow *editor = GTK_WINDOW (widget);
700 	const gchar *title = gtk_window_get_title (editor);
701 	gchar *new_title;
702 	gchar *splitter;
703 	gchar *comp_name, *task_name;
704 
705 	if (!mc)
706 		return;
707 
708 	/* Recursion prevence */
709 	if (mc->editor_title && g_utf8_collate (mc->editor_title, title) == 0)
710 		return;
711 
712 	splitter = strstr (title, "—");
713 	if (!splitter)
714 		return;
715 
716 	comp_name = g_strndup (title, splitter - title - 1);
717 	task_name = g_strdup (splitter + strlen ("—") + 1);
718 	new_title = g_strdup_printf (
719 		"%s (%d/%d) — %s",
720 		comp_name, mc->mails_done, mc->mails_count, task_name);
721 
722 	/* Remember the new title, so that when gtk_window_set_title() causes
723 	 * this handler to be recursively called, we can recognize that and
724 	 * prevent endless recursion */
725 	g_free (mc->editor_title);
726 	mc->editor_title = new_title;
727 
728 	gtk_window_set_title (editor, new_title);
729 
730 	g_free (comp_name);
731 	g_free (task_name);
732 }
733 
734 static gboolean
do_manage_comp_idle(struct _manage_comp * mc)735 do_manage_comp_idle (struct _manage_comp *mc)
736 {
737 	GError *error = NULL;
738 	ECalClientSourceType source_type = E_CAL_CLIENT_SOURCE_TYPE_LAST;
739 	ECalComponent *edit_comp = NULL;
740 
741 	g_return_val_if_fail (mc, FALSE);
742 
743 	source_type = e_cal_client_get_source_type (mc->client);
744 
745 	if (source_type == E_CAL_CLIENT_SOURCE_TYPE_LAST) {
746 		free_manage_comp_struct (mc);
747 
748 		g_warning ("mail-to-task: Incorrect call of %s, no data given", G_STRFUNC);
749 		return FALSE;
750 	}
751 
752 	if (mc->stored_comp) {
753 		const gchar *ask = get_question_edit_old (source_type);
754 
755 		if (ask) {
756 			gchar *msg = g_strdup_printf (ask, i_cal_component_get_summary (mc->stored_comp) ? i_cal_component_get_summary (mc->stored_comp) : _("[No Summary]"));
757 			gint chosen;
758 
759 			chosen = do_ask (msg, TRUE);
760 
761 			if (chosen == GTK_RESPONSE_YES) {
762 				edit_comp = e_cal_component_new ();
763 				if (!e_cal_component_set_icalcomponent (edit_comp, i_cal_component_clone (mc->stored_comp))) {
764 					g_object_unref (edit_comp);
765 					edit_comp = NULL;
766 					error = g_error_new (
767 						E_CAL_CLIENT_ERROR,
768 						E_CAL_CLIENT_ERROR_INVALID_OBJECT,
769 						"%s", _("Invalid object returned from a server"));
770 
771 				}
772 			} else if (chosen == GTK_RESPONSE_NO) {
773 				/* user wants to create a new event, thus generate a new UID */
774 				gchar *new_uid = e_util_generate_uid ();
775 				edit_comp = mc->comp;
776 				e_cal_component_set_uid (edit_comp, new_uid);
777 				e_cal_component_set_recurid (edit_comp, NULL);
778 				g_free (new_uid);
779 			}
780 			g_free (msg);
781 		}
782 	} else {
783 		edit_comp = mc->comp;
784 	}
785 
786 	if (edit_comp) {
787 		EShell *shell;
788 		ECompEditor *comp_editor;
789 
790 		/* FIXME Pass in the EShell instance. */
791 		shell = e_shell_get_default ();
792 		comp_editor = get_component_editor (
793 			shell, mc->client, edit_comp,
794 			edit_comp == mc->comp, &error);
795 
796 		if (comp_editor && !error) {
797 			comp_editor_title_changed (GTK_WIDGET (comp_editor), NULL, mc);
798 
799 			e_signal_connect_notify (
800 				comp_editor, "notify::title",
801 				G_CALLBACK (comp_editor_title_changed), mc);
802 			g_signal_connect (
803 				comp_editor, "editor-closed",
804 				G_CALLBACK (comp_editor_closed), mc);
805 
806 			gtk_window_present (GTK_WINDOW (comp_editor));
807 
808 			if (edit_comp != mc->comp)
809 				g_object_unref (edit_comp);
810 		} else {
811 			g_warning ("Failed to create event editor: %s", error ? error->message : "Unknown error");
812 			g_cond_signal (&mc->cond);
813 		}
814 	} else {
815 		/* User canceled editing already existing event, so
816 		 * treat it as if he just closed the editor window. */
817 		comp_editor_closed (NULL, FALSE, mc);
818 	}
819 
820 	if (error != NULL) {
821 		e_notice (
822 			NULL, GTK_MESSAGE_ERROR,
823 			_("An error occurred during processing: %s"),
824 			error->message);
825 		g_clear_error (&error);
826 	}
827 
828 	return FALSE;
829 }
830 
831 typedef struct {
832 	EClientCache *client_cache;
833 	ESource *source;
834 	const gchar *extension_name;
835 	ECalClientSourceType source_type;
836 	CamelFolder *folder;
837 	GPtrArray *uids;
838 	gchar *selected_text;
839 	gchar *default_charset;
840 	gchar *forced_charset;
841 	gboolean with_attendees;
842 } AsyncData;
843 
844 static void
async_data_free(AsyncData * data)845 async_data_free (AsyncData *data)
846 {
847 	if (data) {
848 		g_free (data->selected_text);
849 		g_free (data->default_charset);
850 		g_free (data->forced_charset);
851 		g_object_unref (data->client_cache);
852 		g_object_unref (data->source);
853 		g_slice_free (AsyncData, data);
854 	}
855 }
856 
857 static gboolean
do_mail_to_event(AsyncData * data)858 do_mail_to_event (AsyncData *data)
859 {
860 	EClient *client;
861 	CamelFolder *folder = data->folder;
862 	GPtrArray *uids = data->uids;
863 	GError *error = NULL;
864 
865 	client = e_client_cache_get_client_sync (data->client_cache,
866 		data->source, data->extension_name, 30, NULL, &error);
867 
868 	/* Sanity check. */
869 	g_return_val_if_fail (
870 		((client != NULL) && (error == NULL)) ||
871 		((client == NULL) && (error != NULL)), TRUE);
872 
873 	if (error != NULL) {
874 		report_error_idle (_("Cannot open calendar. %s"), error->message);
875 	} else if (e_client_is_readonly (E_CLIENT (client))) {
876 		switch (data->source_type) {
877 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
878 			report_error_idle (_("Selected calendar is read only, thus cannot create event there. Select other calendar, please."), NULL);
879 			break;
880 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
881 			report_error_idle (_("Selected task list is read only, thus cannot create task there. Select other task list, please."), NULL);
882 			break;
883 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
884 			report_error_idle (_("Selected memo list is read only, thus cannot create memo there. Select other memo list, please."), NULL);
885 			break;
886 		default:
887 			g_warn_if_reached ();
888 			break;
889 		}
890 	} else {
891 		gint i;
892 		ECalComponentDateTime *dt, *dt2;
893 		ICalTime *tt, *tt2;
894 		struct _manage_comp *oldmc = NULL;
895 
896 		#define cache_backend_prop(prop) { \
897 			gchar *val = NULL; \
898 			e_client_get_backend_property_sync (E_CLIENT (client), prop, &val, NULL, NULL); \
899 			g_free (val); \
900 		}
901 
902 		/* precache backend properties, thus editor have them ready when needed */
903 		cache_backend_prop (E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS);
904 		cache_backend_prop (E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS);
905 		cache_backend_prop (E_CAL_BACKEND_PROPERTY_DEFAULT_OBJECT);
906 		e_client_get_capabilities (E_CLIENT (client));
907 
908 		#undef cache_backend_prop
909 
910 		/* set start day of the event as today, without time - easier than looking for a calendar's time zone */
911 		tt = i_cal_time_new_today ();
912 		tt2 = i_cal_time_clone (tt);
913 		i_cal_time_adjust (tt2, 1, 0, 0, 0);
914 
915 		dt = e_cal_component_datetime_new_take (tt, NULL);
916 		dt2 = e_cal_component_datetime_new_take (tt2, NULL);
917 
918 		for (i = 0; i < (uids ? uids->len : 0); i++) {
919 			CamelMimeMessage *message;
920 			ECalComponent *comp;
921 			ECalComponentText *text;
922 			ICalProperty *prop;
923 			ICalComponent *icomp;
924 			struct _manage_comp *mc;
925 			const gchar *message_uid = g_ptr_array_index (uids, i);
926 
927 			/* retrieve the message from the CamelFolder */
928 			/* FIXME Not passing a GCancellable or GError. */
929 			message = camel_folder_get_message_sync (folder, message_uid, NULL, NULL);
930 			if (!message) {
931 				continue;
932 			}
933 
934 			comp = e_cal_component_new ();
935 
936 			switch (data->source_type) {
937 			case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
938 				e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
939 				break;
940 			case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
941 				e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
942 				break;
943 			case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
944 				e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
945 				break;
946 			default:
947 				g_warn_if_reached ();
948 				break;
949 			}
950 
951 			e_cal_component_set_uid (comp, camel_mime_message_get_message_id (message));
952 			e_cal_component_set_dtstart (comp, dt);
953 
954 			if (data->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
955 				/* make it an all-day event */
956 				e_cal_component_set_dtend (comp, dt2);
957 			}
958 
959 			/* set the summary */
960 			text = e_cal_component_text_new (camel_mime_message_get_subject (message), NULL);
961 			e_cal_component_set_summary (comp, text);
962 			e_cal_component_text_free (text);
963 
964 			/* set all fields */
965 			if (data->selected_text) {
966 				GSList sl;
967 
968 				text = e_cal_component_text_new (data->selected_text, NULL);
969 
970 				sl.next = NULL;
971 				sl.data = text;
972 
973 				e_cal_component_set_descriptions (comp, &sl);
974 
975 				e_cal_component_text_free (text);
976 			} else
977 				set_description (comp, message, data->default_charset, data->forced_charset);
978 
979 			if (data->with_attendees) {
980 				gchar *organizer;
981 
982 				/* set actual user as organizer, to be able to change event's properties */
983 				organizer = set_organizer (comp, message, data->folder, message_uid);
984 				set_attendees (comp, message, organizer);
985 				g_free (organizer);
986 			}
987 
988 			/* set attachment files */
989 			set_attachments (E_CAL_CLIENT (client), comp, message);
990 
991 			/* priority */
992 			set_priority (comp, CAMEL_MIME_PART (message));
993 
994 			/* no need to increment a sequence number, this is a new component */
995 			e_cal_component_abort_sequence (comp);
996 
997 			icomp = e_cal_component_get_icalcomponent (comp);
998 
999 			prop = i_cal_property_new_x ("1");
1000 			i_cal_property_set_x_name (prop, "X-EVOLUTION-MOVE-CALENDAR");
1001 			i_cal_component_take_property (icomp, prop);
1002 
1003 			mc = g_slice_new0 (struct _manage_comp);
1004 			mc->client = E_CAL_CLIENT (g_object_ref (client));
1005 			mc->comp = g_object_ref (comp);
1006 			g_mutex_init (&mc->mutex);
1007 			g_cond_init (&mc->cond);
1008 			mc->mails_count = uids->len;
1009 			mc->mails_done = i + 1; /* Current task */
1010 			mc->editor_title = NULL;
1011 			mc->can_continue = TRUE;
1012 
1013 			if (oldmc) {
1014 				/* Wait for user to quit the editor created in previous iteration
1015 				 * before displaying next one */
1016 				gboolean can_continue;
1017 				g_mutex_lock (&oldmc->mutex);
1018 				g_cond_wait (&oldmc->cond, &oldmc->mutex);
1019 				g_mutex_unlock (&oldmc->mutex);
1020 				can_continue = oldmc->can_continue;
1021 				free_manage_comp_struct (oldmc);
1022 				oldmc = NULL;
1023 
1024 				if (!can_continue)
1025 					break;
1026 			}
1027 
1028 			e_cal_client_get_object_sync (
1029 				E_CAL_CLIENT (client),
1030 				i_cal_component_get_uid (icomp),
1031 				NULL, &mc->stored_comp, NULL, NULL);
1032 
1033 			/* Prioritize ahead of GTK+ redraws. */
1034 			g_idle_add_full (
1035 				G_PRIORITY_HIGH_IDLE,
1036 				(GSourceFunc) do_manage_comp_idle, mc, NULL);
1037 
1038 			oldmc = mc;
1039 
1040 			g_object_unref (comp);
1041 			g_object_unref (message);
1042 
1043 		}
1044 
1045 		/* Wait for the last editor and then clean up */
1046 		if (oldmc) {
1047 			g_mutex_lock (&oldmc->mutex);
1048 			g_cond_wait (&oldmc->cond, &oldmc->mutex);
1049 			g_mutex_unlock (&oldmc->mutex);
1050 			free_manage_comp_struct (oldmc);
1051 		}
1052 
1053 		e_cal_component_datetime_free (dt);
1054 		e_cal_component_datetime_free (dt2);
1055 	}
1056 
1057 	/* free memory */
1058 	if (client != NULL)
1059 		g_object_unref (client);
1060 	g_ptr_array_unref (uids);
1061 	g_object_unref (folder);
1062 
1063 	async_data_free (data);
1064 	g_clear_error (&error);
1065 
1066 	return TRUE;
1067 }
1068 
1069 static gboolean
text_contains_nonwhitespace(const gchar * text,gint len)1070 text_contains_nonwhitespace (const gchar *text,
1071                              gint len)
1072 {
1073 	const gchar *p;
1074 	gunichar c = 0;
1075 
1076 	if (!text || len <= 0)
1077 		return FALSE;
1078 
1079 	p = text;
1080 
1081 	while (p && p - text < len) {
1082 		c = g_utf8_get_char (p);
1083 		if (!c)
1084 			break;
1085 
1086 		if (!g_unichar_isspace (c))
1087 			break;
1088 
1089 		p = g_utf8_next_char (p);
1090 	}
1091 
1092 	return p - text < len - 1 && c != 0;
1093 }
1094 
1095 static void
get_charsets(EMailReader * reader,gchar ** default_charset,gchar ** forced_charset)1096 get_charsets (EMailReader *reader,
1097 	      gchar **default_charset,
1098 	      gchar **forced_charset)
1099 {
1100 	EMailDisplay *display;
1101 	EMailFormatter *formatter;
1102 
1103 	display = e_mail_reader_get_mail_display (reader);
1104 	formatter = e_mail_display_get_formatter (display);
1105 
1106 	*default_charset = e_mail_formatter_dup_default_charset (formatter);
1107 	*forced_charset = e_mail_formatter_dup_charset (formatter);
1108 }
1109 
1110 static void
start_mail_to_event_thread(AsyncData * data)1111 start_mail_to_event_thread (AsyncData *data)
1112 {
1113 	GThread *thread = NULL;
1114 	GError *error = NULL;
1115 
1116 	thread = g_thread_try_new (NULL, (GThreadFunc) do_mail_to_event, data, &error);
1117 
1118 	if (error != NULL) {
1119 		g_warning (G_STRLOC ": %s", error->message);
1120 		g_error_free (error);
1121 		async_data_free (data);
1122 	} else {
1123 		g_thread_unref (thread);
1124 	}
1125 }
1126 
1127 static void
mail_to_task_got_selection_jsc_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1128 mail_to_task_got_selection_jsc_cb (GObject *source_object,
1129 				   GAsyncResult *result,
1130 				   gpointer user_data)
1131 {
1132 	AsyncData *data = user_data;
1133 	GSList *texts = NULL;
1134 	gchar *text;
1135 	GError *error = NULL;
1136 
1137 	g_return_if_fail (data != NULL);
1138 	g_return_if_fail (E_IS_WEB_VIEW (source_object));
1139 
1140 	if (!e_web_view_jsc_get_selection_finish (WEBKIT_WEB_VIEW (source_object), result, &texts, &error)) {
1141 		texts = NULL;
1142 		g_warning ("%s: Failed to get view selection: %s", G_STRFUNC, error ? error->message : "Unknown error");
1143 	}
1144 
1145 	text = texts ? texts->data : NULL;
1146 
1147 	if (text && !text_contains_nonwhitespace (text, strlen (text))) {
1148 		text = NULL;
1149 	} else {
1150 		/* Steal the pointer */
1151 		if (texts)
1152 			texts->data = NULL;
1153 	}
1154 
1155 	data->selected_text = text;
1156 
1157 	start_mail_to_event_thread (data);
1158 
1159 	g_slist_free_full (texts, g_free);
1160 	g_clear_error (&error);
1161 }
1162 
1163 static void
mail_to_event(ECalClientSourceType source_type,gboolean with_attendees,EMailReader * reader)1164 mail_to_event (ECalClientSourceType source_type,
1165                gboolean with_attendees,
1166                EMailReader *reader)
1167 {
1168 	EShell *shell;
1169 	EMailBackend *backend;
1170 	ESourceRegistry *registry;
1171 	GPtrArray *uids;
1172 	ESource *source = NULL;
1173 	ESource *default_source;
1174 	GList *list, *iter;
1175 	GtkWindow *parent;
1176 	const gchar *extension_name;
1177 
1178 	parent = e_mail_reader_get_window (reader);
1179 	uids = e_mail_reader_get_selected_uids (reader);
1180 
1181 	/* Ask before converting 10 or more mails to events. */
1182 	if (uids->len > 10) {
1183 		gchar *question;
1184 		gint response;
1185 
1186 		question = g_strdup_printf (
1187 			get_question_add_all_mails (source_type, uids->len), uids->len);
1188 		response = do_ask (question, FALSE);
1189 		g_free (question);
1190 
1191 		if (response == GTK_RESPONSE_NO) {
1192 			g_ptr_array_unref (uids);
1193 			return;
1194 		}
1195 	}
1196 
1197 	backend = e_mail_reader_get_backend (reader);
1198 	shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
1199 	registry = e_shell_get_registry (shell);
1200 
1201 	switch (source_type) {
1202 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1203 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
1204 			default_source = e_source_registry_ref_default_calendar (registry);
1205 			break;
1206 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1207 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1208 			default_source = e_source_registry_ref_default_memo_list (registry);
1209 			break;
1210 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1211 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1212 			default_source = e_source_registry_ref_default_task_list (registry);
1213 			break;
1214 		default:
1215 			g_return_if_reached ();
1216 	}
1217 
1218 	list = e_source_registry_list_sources (registry, extension_name);
1219 
1220 	/* If there is only one writable source, no need to prompt the user. */
1221 	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1222 		ESource *candidate = E_SOURCE (iter->data);
1223 
1224 		if (e_source_get_writable (candidate)) {
1225 			if (source == NULL)
1226 				source = candidate;
1227 			else {
1228 				source = NULL;
1229 				break;
1230 			}
1231 		}
1232 	}
1233 
1234 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1235 
1236 	if (source == NULL) {
1237 		GtkWidget *dialog;
1238 		ESourceSelector *selector;
1239 
1240 		/* ask the user which tasks list to save to */
1241 		dialog = e_source_selector_dialog_new (
1242 			parent, registry, extension_name);
1243 
1244 		selector = e_source_selector_dialog_get_selector (
1245 			E_SOURCE_SELECTOR_DIALOG (dialog));
1246 
1247 		e_source_selector_set_primary_selection (
1248 			selector, default_source);
1249 
1250 		if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
1251 			source = e_source_selector_dialog_peek_primary_selection (
1252 				E_SOURCE_SELECTOR_DIALOG (dialog));
1253 
1254 		gtk_widget_destroy (dialog);
1255 	}
1256 
1257 	if (source) {
1258 		/* if a source has been selected, perform the mail2event operation */
1259 		AsyncData *data;
1260 		EMailDisplay *mail_display;
1261 
1262 		/* Fill the elements in AsynData */
1263 		data = g_slice_new0 (AsyncData);
1264 		data->client_cache = g_object_ref (e_shell_get_client_cache (shell));
1265 		data->source = g_object_ref (source);
1266 		data->extension_name = extension_name;
1267 		data->source_type = source_type;
1268 		data->folder = e_mail_reader_ref_folder (reader);
1269 		data->uids = g_ptr_array_ref (uids);
1270 		data->with_attendees = with_attendees;
1271 		get_charsets (reader, &data->default_charset, &data->forced_charset);
1272 
1273 		mail_display = e_mail_reader_get_mail_display (reader);
1274 
1275 		if (uids->len == 1 && e_web_view_has_selection (E_WEB_VIEW (mail_display))) {
1276 			e_web_view_jsc_get_selection (WEBKIT_WEB_VIEW (mail_display), E_TEXT_FORMAT_PLAIN, NULL,
1277 				mail_to_task_got_selection_jsc_cb, data);
1278 		} else {
1279 			data->selected_text = NULL;
1280 
1281 			start_mail_to_event_thread (data);
1282 		}
1283 	}
1284 
1285 	g_object_unref (default_source);
1286 	g_ptr_array_unref (uids);
1287 }
1288 
1289 static void
action_mail_convert_to_event_cb(GtkAction * action,EMailReader * reader)1290 action_mail_convert_to_event_cb (GtkAction *action,
1291                                  EMailReader *reader)
1292 {
1293 	mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, FALSE, reader);
1294 }
1295 
1296 static void
action_mail_convert_to_meeting_cb(GtkAction * action,EMailReader * reader)1297 action_mail_convert_to_meeting_cb (GtkAction *action,
1298                                    EMailReader *reader)
1299 {
1300 	mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, TRUE, reader);
1301 }
1302 
1303 static void
action_mail_convert_to_memo_cb(GtkAction * action,EMailReader * reader)1304 action_mail_convert_to_memo_cb (GtkAction *action,
1305                                 EMailReader *reader)
1306 {
1307 	mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_MEMOS, FALSE, reader);
1308 }
1309 
1310 static void
action_mail_convert_to_task_cb(GtkAction * action,EMailReader * reader)1311 action_mail_convert_to_task_cb (GtkAction *action,
1312                                 EMailReader *reader)
1313 {
1314 	mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_TASKS, FALSE, reader);
1315 }
1316 
1317 /* Note, we're not using EPopupActions here because we update the state
1318  * of entire actions groups instead of individual actions.  EPopupActions
1319  * just proxy the state of individual actions. */
1320 
1321 static GtkActionEntry multi_selection_entries[] = {
1322 
1323 	{ "mail-convert-to-appointment",
1324 	  "appointment-new",
1325 	  N_("Create an _Appointment"),
1326 	  NULL,
1327 	  N_("Create a new event from the selected message"),
1328 	  G_CALLBACK (action_mail_convert_to_event_cb) },
1329 
1330 	{ "mail-convert-to-memo",
1331 	  "stock_insert-note",
1332 	  N_("Create a Mem_o"),
1333 	  NULL,
1334 	  N_("Create a new memo from the selected message"),
1335 	  G_CALLBACK (action_mail_convert_to_memo_cb) },
1336 
1337 	{ "mail-convert-to-task",
1338 	  "stock_todo",
1339 	  N_("Create a _Task"),
1340 	  NULL,
1341 	  N_("Create a new task from the selected message"),
1342 	  G_CALLBACK (action_mail_convert_to_task_cb) }
1343 };
1344 
1345 static GtkActionEntry single_selection_entries[] = {
1346 
1347 	{ "mail-convert-to-meeting",
1348 	  "stock_people",
1349 	  N_("Create a _Meeting"),
1350 	  NULL,
1351 	  N_("Create a new meeting from the selected message"),
1352 	  G_CALLBACK (action_mail_convert_to_meeting_cb) }
1353 };
1354 
1355 static void
update_actions_any_cb(EMailReader * reader,guint32 state,GtkActionGroup * action_group)1356 update_actions_any_cb (EMailReader *reader,
1357                        guint32 state,
1358                        GtkActionGroup *action_group)
1359 {
1360 	gboolean sensitive;
1361 
1362 	sensitive =
1363 		(state & E_MAIL_READER_SELECTION_SINGLE) ||
1364 		(state & E_MAIL_READER_SELECTION_MULTIPLE);
1365 
1366 	gtk_action_group_set_sensitive (action_group, sensitive);
1367 }
1368 
1369 static void
update_actions_one_cb(EMailReader * reader,guint32 state,GtkActionGroup * action_group)1370 update_actions_one_cb (EMailReader *reader,
1371                        guint32 state,
1372                        GtkActionGroup *action_group)
1373 {
1374 	gboolean sensitive;
1375 
1376 	sensitive = (state & E_MAIL_READER_SELECTION_SINGLE);
1377 
1378 	gtk_action_group_set_sensitive (action_group, sensitive);
1379 }
1380 
1381 static void
setup_actions(EMailReader * reader,GtkUIManager * ui_manager)1382 setup_actions (EMailReader *reader,
1383                GtkUIManager *ui_manager)
1384 {
1385 	GtkActionGroup *action_group;
1386 	const gchar *domain = GETTEXT_PACKAGE;
1387 
1388 	action_group = gtk_action_group_new ("mail-convert-any");
1389 	gtk_action_group_set_translation_domain (action_group, domain);
1390 	gtk_action_group_add_actions (
1391 		action_group, multi_selection_entries,
1392 		G_N_ELEMENTS (multi_selection_entries), reader);
1393 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1394 	g_object_unref (action_group);
1395 
1396 	/* GtkUIManager now owns the action group reference.
1397 	 * The signal we're connecting to will only be emitted
1398 	 * during the GtkUIManager's lifetime, so the action
1399 	 * group will not disappear on us. */
1400 
1401 	g_signal_connect (
1402 		reader, "update-actions",
1403 		G_CALLBACK (update_actions_any_cb), action_group);
1404 
1405 	action_group = gtk_action_group_new ("mail-convert-one");
1406 	gtk_action_group_set_translation_domain (action_group, domain);
1407 	gtk_action_group_add_actions (
1408 		action_group, single_selection_entries,
1409 		G_N_ELEMENTS (single_selection_entries), reader);
1410 	gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
1411 	g_object_unref (action_group);
1412 
1413 	/* GtkUIManager now owns the action group reference.
1414 	 * The signal we're connecting to will only be emitted
1415 	 * during the GtkUIManager's lifetime, so the action
1416 	 * group will not disappear on us. */
1417 
1418 	g_signal_connect (
1419 		reader, "update-actions",
1420 		G_CALLBACK (update_actions_one_cb), action_group);
1421 }
1422 
1423 gboolean
mail_browser_init(GtkUIManager * ui_manager,EMailBrowser * browser)1424 mail_browser_init (GtkUIManager *ui_manager,
1425                    EMailBrowser *browser)
1426 {
1427 	setup_actions (E_MAIL_READER (browser), ui_manager);
1428 
1429 	return TRUE;
1430 }
1431 
1432 gboolean
mail_shell_view_init(GtkUIManager * ui_manager,EShellView * shell_view)1433 mail_shell_view_init (GtkUIManager *ui_manager,
1434                       EShellView *shell_view)
1435 {
1436 	EShellContent *shell_content;
1437 
1438 	shell_content = e_shell_view_get_shell_content (shell_view);
1439 
1440 	setup_actions (E_MAIL_READER (shell_content), ui_manager);
1441 
1442 	return TRUE;
1443 }
1444