JavaScript is disabled in EWebView, so any attempt to evaluate a JavaScript code will fail. We are using DOM bindings instead to interact with the document. This commit removes some helper functions created in the early days of WebKit port which are not used anymore and also fixes mail-to-task plugin, which was relying on some JavaScript.
1389 lines
36 KiB
C
1389 lines
36 KiB
C
/*
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with the program; if not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Michael Zucchi <notzed@novell.com>
|
|
* Rodrigo Moya <rodrigo@novell.com>
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
*/
|
|
|
|
/* Convert a mail message into a task */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include <libecal/libecal.h>
|
|
#include <libedataserverui/libedataserverui.h>
|
|
|
|
#include <libemail-engine/e-mail-utils.h>
|
|
|
|
#include <e-util/e-dialog-utils.h>
|
|
|
|
#include <misc/e-popup-action.h>
|
|
#include <misc/e-attachment-store.h>
|
|
|
|
#include <shell/e-shell-view.h>
|
|
#include <shell/e-shell-window-actions.h>
|
|
|
|
#include <mail/e-mail-browser.h>
|
|
#include <mail/em-utils.h>
|
|
#include <mail/message-list.h>
|
|
|
|
#include <calendar/gui/dialogs/comp-editor.h>
|
|
#include <calendar/gui/dialogs/event-editor.h>
|
|
#include <calendar/gui/dialogs/memo-editor.h>
|
|
#include <calendar/gui/dialogs/task-editor.h>
|
|
|
|
#define E_SHELL_WINDOW_ACTION_CONVERT_TO_APPOINTMENT(window) \
|
|
E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-appointment")
|
|
#define E_SHELL_WINDOW_ACTION_CONVERT_TO_MEETING(window) \
|
|
E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-meeting")
|
|
#define E_SHELL_WINDOW_ACTION_CONVERT_TO_MEMO(window) \
|
|
E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-memo")
|
|
#define E_SHELL_WINDOW_ACTION_CONVERT_TO_TASK(window) \
|
|
E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-task")
|
|
|
|
gboolean mail_browser_init (GtkUIManager *ui_manager,
|
|
EMailBrowser *browser);
|
|
gboolean mail_shell_view_init (GtkUIManager *ui_manager,
|
|
EShellView *shell_view);
|
|
|
|
static CompEditor *
|
|
get_component_editor (EShell *shell,
|
|
ECalClient *client,
|
|
ECalComponent *comp,
|
|
gboolean is_new,
|
|
GError **error)
|
|
{
|
|
ECalComponentId *id;
|
|
CompEditorFlags flags = 0;
|
|
CompEditor *editor = NULL;
|
|
ESourceRegistry *registry;
|
|
|
|
g_return_val_if_fail (E_IS_SHELL (shell), NULL);
|
|
g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
|
|
g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), NULL);
|
|
|
|
registry = e_shell_get_registry (shell);
|
|
|
|
id = e_cal_component_get_id (comp);
|
|
g_return_val_if_fail (id != NULL, NULL);
|
|
g_return_val_if_fail (id->uid != NULL, NULL);
|
|
|
|
if (is_new) {
|
|
flags |= COMP_EDITOR_NEW_ITEM;
|
|
} else {
|
|
editor = comp_editor_find_instance (id->uid);
|
|
}
|
|
|
|
if (!editor) {
|
|
if (itip_organizer_is_user (registry, comp, client))
|
|
flags |= COMP_EDITOR_USER_ORG;
|
|
|
|
switch (e_cal_component_get_vtype (comp)) {
|
|
case E_CAL_COMPONENT_EVENT:
|
|
if (e_cal_component_has_attendees (comp))
|
|
flags |= COMP_EDITOR_MEETING;
|
|
|
|
editor = event_editor_new (client, shell, flags);
|
|
|
|
if (flags & COMP_EDITOR_MEETING)
|
|
event_editor_show_meeting (EVENT_EDITOR (editor));
|
|
break;
|
|
case E_CAL_COMPONENT_TODO:
|
|
if (e_cal_component_has_attendees (comp))
|
|
flags |= COMP_EDITOR_IS_ASSIGNED;
|
|
|
|
editor = task_editor_new (client, shell, flags);
|
|
|
|
if (flags & COMP_EDITOR_IS_ASSIGNED)
|
|
task_editor_show_assignment (TASK_EDITOR (editor));
|
|
break;
|
|
case E_CAL_COMPONENT_JOURNAL:
|
|
if (e_cal_component_has_organizer (comp))
|
|
flags |= COMP_EDITOR_IS_SHARED;
|
|
|
|
editor = memo_editor_new (client, shell, flags);
|
|
break;
|
|
default:
|
|
if (error)
|
|
*error = e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL);
|
|
break;
|
|
}
|
|
|
|
if (editor) {
|
|
comp_editor_edit_comp (editor, comp);
|
|
|
|
/* request save for new events */
|
|
comp_editor_set_changed (editor, is_new);
|
|
}
|
|
}
|
|
|
|
e_cal_component_free_id (id);
|
|
|
|
return editor;
|
|
}
|
|
|
|
static void
|
|
set_attendees (ECalComponent *comp,
|
|
CamelMimeMessage *message,
|
|
const gchar *organizer)
|
|
{
|
|
GSList *attendees = NULL, *to_free = NULL;
|
|
ECalComponentAttendee *ca;
|
|
CamelInternetAddress *from = NULL, *to, *cc, *bcc, *arr[4];
|
|
gint len, i, j;
|
|
|
|
if (message->reply_to)
|
|
from = message->reply_to;
|
|
else if (message->from)
|
|
from = message->from;
|
|
|
|
to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
|
|
cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
|
|
bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
|
|
|
|
arr[0] = from; arr[1] = to; arr[2] = cc; arr[3] = bcc;
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
if (!arr[j])
|
|
continue;
|
|
|
|
len = CAMEL_ADDRESS (arr[j])->addresses->len;
|
|
for (i = 0; i < len; i++) {
|
|
const gchar *name, *addr;
|
|
|
|
if (camel_internet_address_get (arr[j], i, &name, &addr)) {
|
|
gchar *temp;
|
|
|
|
temp = g_strconcat ("mailto:", addr, NULL);
|
|
if (organizer && g_ascii_strcasecmp (temp, organizer) == 0) {
|
|
/* do not add organizer twice */
|
|
g_free (temp);
|
|
continue;
|
|
}
|
|
|
|
ca = g_new0 (ECalComponentAttendee, 1);
|
|
|
|
ca->value = temp;
|
|
ca->cn = name;
|
|
ca->cutype = ICAL_CUTYPE_INDIVIDUAL;
|
|
ca->status = ICAL_PARTSTAT_NEEDSACTION;
|
|
if (j == 0) {
|
|
/* From */
|
|
ca->role = ICAL_ROLE_CHAIR;
|
|
} else if (j == 2) {
|
|
/* BCC */
|
|
ca->role = ICAL_ROLE_OPTPARTICIPANT;
|
|
} else {
|
|
/* all other */
|
|
ca->role = ICAL_ROLE_REQPARTICIPANT;
|
|
}
|
|
|
|
to_free = g_slist_prepend (to_free, temp);
|
|
|
|
attendees = g_slist_append (attendees, ca);
|
|
}
|
|
}
|
|
}
|
|
|
|
e_cal_component_set_attendee_list (comp, attendees);
|
|
|
|
g_slist_foreach (attendees, (GFunc) g_free, NULL);
|
|
g_slist_foreach (to_free, (GFunc) g_free, NULL);
|
|
|
|
g_slist_free (to_free);
|
|
g_slist_free (attendees);
|
|
}
|
|
|
|
static const gchar *
|
|
prepend_from (CamelMimeMessage *message,
|
|
gchar **text)
|
|
{
|
|
gchar *res, *tmp, *addr = NULL;
|
|
const gchar *name = NULL, *eml = NULL;
|
|
CamelInternetAddress *from = NULL;
|
|
|
|
g_return_val_if_fail (message != NULL, NULL);
|
|
g_return_val_if_fail (text != NULL, NULL);
|
|
|
|
if (message->reply_to)
|
|
from = message->reply_to;
|
|
else if (message->from)
|
|
from = message->from;
|
|
|
|
if (from && camel_internet_address_get (from, 0, &name, &eml))
|
|
addr = camel_internet_address_format_address (name, eml);
|
|
|
|
/* To Translators: The full sentence looks like: "Created from a mail by John Doe <john.doe@myco.example>" */
|
|
tmp = g_strdup_printf (_("Created from a mail by %s"), addr ? addr : "");
|
|
|
|
res = g_strconcat (tmp, "\n", *text, NULL);
|
|
|
|
g_free (tmp);
|
|
g_free (*text);
|
|
|
|
*text = res;
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
set_description (ECalComponent *comp,
|
|
CamelMimeMessage *message)
|
|
{
|
|
CamelDataWrapper *content;
|
|
CamelStream *stream;
|
|
CamelContentType *type;
|
|
CamelMimePart *mime_part = CAMEL_MIME_PART (message);
|
|
ECalComponentText *text = NULL;
|
|
GByteArray *byte_array;
|
|
GSList *sl = NULL;
|
|
gchar *str, *convert_str = NULL;
|
|
gsize bytes_read, bytes_written;
|
|
gint count = 2;
|
|
|
|
content = camel_medium_get_content ((CamelMedium *) message);
|
|
if (!content)
|
|
return;
|
|
|
|
/*
|
|
* Get non-multipart content from multipart message.
|
|
*/
|
|
while (CAMEL_IS_MULTIPART (content) && count > 0) {
|
|
mime_part = camel_multipart_get_part (CAMEL_MULTIPART (content), 0);
|
|
content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
|
|
count--;
|
|
}
|
|
|
|
if (!mime_part)
|
|
return;
|
|
|
|
type = camel_mime_part_get_content_type (mime_part);
|
|
if (!camel_content_type_is (type, "text", "plain"))
|
|
return;
|
|
|
|
byte_array = g_byte_array_new ();
|
|
stream = camel_stream_mem_new_with_byte_array (byte_array);
|
|
camel_data_wrapper_decode_to_stream_sync (content, stream, NULL, NULL);
|
|
str = g_strndup ((gchar *) byte_array->data, byte_array->len);
|
|
g_object_unref (stream);
|
|
|
|
/* convert to UTF-8 string */
|
|
if (str && content->mime_type->params && content->mime_type->params->value) {
|
|
convert_str = g_convert (str, strlen (str),
|
|
"UTF-8", content->mime_type->params->value,
|
|
&bytes_read, &bytes_written, NULL);
|
|
}
|
|
|
|
text = g_new0 (ECalComponentText, 1);
|
|
if (convert_str)
|
|
text->value = prepend_from (message, &convert_str);
|
|
else
|
|
text->value = prepend_from (message, &str);
|
|
text->altrep = NULL;
|
|
sl = g_slist_append (sl, text);
|
|
|
|
e_cal_component_set_description_list (comp, sl);
|
|
|
|
g_free (str);
|
|
if (convert_str)
|
|
g_free (convert_str);
|
|
e_cal_component_free_text_list (sl);
|
|
}
|
|
|
|
static gchar *
|
|
set_organizer (ECalComponent *comp,
|
|
CamelFolder *folder)
|
|
{
|
|
EShell *shell;
|
|
ESource *source = NULL;
|
|
ESourceRegistry *registry;
|
|
ESourceMailIdentity *extension;
|
|
const gchar *extension_name;
|
|
const gchar *address, *name;
|
|
ECalComponentOrganizer organizer = {NULL, NULL, NULL, NULL};
|
|
gchar *mailto = NULL;
|
|
|
|
shell = e_shell_get_default ();
|
|
registry = e_shell_get_registry (shell);
|
|
|
|
if (folder != NULL) {
|
|
CamelStore *store;
|
|
|
|
store = camel_folder_get_parent_store (folder);
|
|
source = em_utils_ref_mail_identity_for_store (registry, store);
|
|
}
|
|
|
|
if (source == NULL)
|
|
source = e_source_registry_ref_default_mail_identity (registry);
|
|
|
|
g_return_val_if_fail (source != NULL, NULL);
|
|
|
|
extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
|
|
extension = e_source_get_extension (source, extension_name);
|
|
|
|
name = e_source_mail_identity_get_name (extension);
|
|
address = e_source_mail_identity_get_address (extension);
|
|
|
|
if (name != NULL && address != NULL) {
|
|
mailto = g_strconcat ("mailto:", address, NULL);
|
|
organizer.value = mailto;
|
|
organizer.cn = name;
|
|
e_cal_component_set_organizer (comp, &organizer);
|
|
}
|
|
|
|
g_object_unref (source);
|
|
|
|
return mailto;
|
|
}
|
|
|
|
struct _att_async_cb_data {
|
|
gchar **uris;
|
|
EFlag *flag;
|
|
};
|
|
|
|
static void
|
|
attachment_load_finished (EAttachmentStore *store,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
struct _att_async_cb_data *data = user_data;
|
|
|
|
/* XXX Should be no need to check for error here.
|
|
* This is just to reset state in the EAttachment. */
|
|
e_attachment_store_load_finish (store, result, NULL);
|
|
|
|
e_flag_set (data->flag);
|
|
}
|
|
|
|
static void
|
|
attachment_save_finished (EAttachmentStore *store,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
struct _att_async_cb_data *data = user_data;
|
|
gchar **uris;
|
|
GError *error = NULL;
|
|
|
|
uris = e_attachment_store_save_finish (store, result, &error);
|
|
if (error)
|
|
data->uris = NULL;
|
|
else
|
|
data->uris = uris;
|
|
|
|
g_clear_error (&error);
|
|
|
|
e_flag_set (data->flag);
|
|
}
|
|
|
|
static void
|
|
set_attachments (ECalClient *client,
|
|
ECalComponent *comp,
|
|
CamelMimeMessage *message)
|
|
{
|
|
/* XXX Much of this is copied from CompEditor::get_attachment_list().
|
|
* Perhaps it should be split off as a separate utility? */
|
|
|
|
EAttachmentStore *store;
|
|
CamelDataWrapper *content;
|
|
CamelMultipart *multipart;
|
|
GFile *destination;
|
|
GList *attachment_list = NULL;
|
|
GSList *uri_list = NULL;
|
|
const gchar *comp_uid = NULL;
|
|
const gchar *local_store;
|
|
gchar *filename_prefix, *tmp;
|
|
gint ii, n_parts;
|
|
struct _att_async_cb_data cb_data;
|
|
|
|
cb_data.flag = e_flag_new ();
|
|
cb_data.uris = NULL;
|
|
|
|
content = camel_medium_get_content ((CamelMedium *) message);
|
|
if (!content || !CAMEL_IS_MULTIPART (content))
|
|
return;
|
|
|
|
n_parts = camel_multipart_get_number (CAMEL_MULTIPART (content));
|
|
if (n_parts < 1)
|
|
return;
|
|
|
|
e_cal_component_get_uid (comp, &comp_uid);
|
|
g_return_if_fail (comp_uid != NULL);
|
|
|
|
tmp = g_strdup (comp_uid);
|
|
e_filename_make_safe (tmp);
|
|
filename_prefix = g_strconcat (tmp, "-", NULL);
|
|
g_free (tmp);
|
|
|
|
local_store = e_cal_client_get_local_attachment_store (client);
|
|
destination = g_file_new_for_path (local_store);
|
|
|
|
/* Create EAttachments from the MIME parts and add them to the
|
|
* attachment store. */
|
|
|
|
multipart = CAMEL_MULTIPART (content);
|
|
store = E_ATTACHMENT_STORE (e_attachment_store_new ());
|
|
|
|
for (ii = 1; ii < n_parts; ii++) {
|
|
EAttachment *attachment;
|
|
CamelMimePart *mime_part;
|
|
|
|
attachment = e_attachment_new ();
|
|
mime_part = camel_multipart_get_part (multipart, ii);
|
|
e_attachment_set_mime_part (attachment, mime_part);
|
|
|
|
attachment_list = g_list_append (attachment_list, attachment);
|
|
}
|
|
|
|
e_flag_clear (cb_data.flag);
|
|
|
|
e_attachment_store_load_async (
|
|
store, attachment_list, (GAsyncReadyCallback)
|
|
attachment_load_finished, &cb_data);
|
|
|
|
/* Loading should be instantaneous since we already have
|
|
* the full content, but we need to wait for the callback.
|
|
*/
|
|
e_flag_wait (cb_data.flag);
|
|
|
|
g_list_foreach (attachment_list, (GFunc) g_object_unref, NULL);
|
|
g_list_free (attachment_list);
|
|
|
|
cb_data.uris = NULL;
|
|
e_flag_clear (cb_data.flag);
|
|
|
|
e_attachment_store_save_async (
|
|
store, destination, filename_prefix,
|
|
(GAsyncReadyCallback) attachment_save_finished, &cb_data);
|
|
|
|
g_free (filename_prefix);
|
|
|
|
/* We can't return until we have results. */
|
|
e_flag_wait (cb_data.flag);
|
|
|
|
if (cb_data.uris == NULL) {
|
|
e_flag_free (cb_data.flag);
|
|
g_warning ("No attachment URIs retrieved.");
|
|
return;
|
|
}
|
|
|
|
/* Transfer the URI strings to the GSList. */
|
|
for (ii = 0; cb_data.uris[ii] != NULL; ii++) {
|
|
uri_list = g_slist_prepend (uri_list, cb_data.uris[ii]);
|
|
cb_data.uris[ii] = NULL;
|
|
}
|
|
|
|
e_flag_free (cb_data.flag);
|
|
g_free (cb_data.uris);
|
|
|
|
/* XXX Does this take ownership of the list? */
|
|
e_cal_component_set_attachment_list (comp, uri_list);
|
|
|
|
e_attachment_store_remove_all (store);
|
|
g_object_unref (destination);
|
|
g_object_unref (store);
|
|
}
|
|
|
|
static void
|
|
set_priority (ECalComponent *comp,
|
|
CamelMimePart *part)
|
|
{
|
|
const gchar *prio;
|
|
|
|
g_return_if_fail (comp != NULL);
|
|
g_return_if_fail (part != NULL);
|
|
|
|
prio = camel_header_raw_find (& (part->headers), "X-Priority", NULL);
|
|
if (prio && atoi (prio) > 0) {
|
|
gint priority = 1;
|
|
|
|
e_cal_component_set_priority (comp, &priority);
|
|
}
|
|
}
|
|
|
|
struct _report_error
|
|
{
|
|
gchar *format;
|
|
gchar *param;
|
|
};
|
|
|
|
static gboolean
|
|
do_report_error (struct _report_error *err)
|
|
{
|
|
if (err) {
|
|
e_notice (NULL, GTK_MESSAGE_ERROR, err->format, err->param);
|
|
g_free (err->format);
|
|
g_free (err->param);
|
|
g_free (err);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
report_error_idle (const gchar *format,
|
|
const gchar *param)
|
|
{
|
|
struct _report_error *err = g_new (struct _report_error, 1);
|
|
|
|
err->format = g_strdup (format);
|
|
err->param = g_strdup (param);
|
|
|
|
g_usleep (250);
|
|
g_idle_add ((GSourceFunc) do_report_error, err);
|
|
}
|
|
|
|
struct _manage_comp
|
|
{
|
|
ECalClient *client;
|
|
ECalComponent *comp;
|
|
icalcomponent *stored_comp; /* the one in client already */
|
|
GCond *cond;
|
|
GMutex *mutex;
|
|
gint mails_count;
|
|
gint mails_done;
|
|
gchar *editor_title;
|
|
gboolean can_continue;
|
|
};
|
|
|
|
static void
|
|
free_manage_comp_struct (struct _manage_comp *mc)
|
|
{
|
|
g_return_if_fail (mc != NULL);
|
|
|
|
g_object_unref (mc->comp);
|
|
g_object_unref (mc->client);
|
|
if (mc->stored_comp)
|
|
icalcomponent_free (mc->stored_comp);
|
|
if (mc->mutex)
|
|
g_mutex_free (mc->mutex);
|
|
if (mc->cond)
|
|
g_cond_free (mc->cond);
|
|
if (mc->editor_title)
|
|
g_free (mc->editor_title);
|
|
|
|
g_free (mc);
|
|
}
|
|
|
|
static gint
|
|
do_ask (const gchar *text,
|
|
gboolean is_create_edit_add)
|
|
{
|
|
gint res;
|
|
GtkWidget *dialog = gtk_message_dialog_new (NULL,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_QUESTION,
|
|
is_create_edit_add ? GTK_BUTTONS_NONE : GTK_BUTTONS_YES_NO,
|
|
"%s", text);
|
|
|
|
if (is_create_edit_add) {
|
|
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
GTK_STOCK_EDIT, GTK_RESPONSE_YES,
|
|
GTK_STOCK_NEW, GTK_RESPONSE_NO,
|
|
NULL);
|
|
}
|
|
|
|
res = gtk_dialog_run (GTK_DIALOG (dialog));
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
return res;
|
|
}
|
|
|
|
static const gchar *
|
|
get_question_edit_old (ECalClientSourceType source_type)
|
|
{
|
|
const gchar *ask = NULL;
|
|
|
|
switch (source_type) {
|
|
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
|
|
ask = _("Selected calendar contains event '%s' already. Would you like to edit the old event?");
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
|
|
ask = _("Selected task list contains task '%s' already. Would you like to edit the old task?");
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
|
|
ask = _("Selected memo list contains memo '%s' already. Would you like to edit the old memo?");
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
return ask;
|
|
}
|
|
|
|
static const gchar *
|
|
get_question_add_all_mails (ECalClientSourceType source_type,
|
|
gint count)
|
|
{
|
|
const gchar *ask = NULL;
|
|
|
|
switch (source_type) {
|
|
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
|
|
/* Translators: Note there are always more than 10 mails selected */
|
|
ask = ngettext ("You have selected %d mails to be converted to events. Do you really want to add them all?",
|
|
"You have selected %d mails to be converted to events. Do you really want to add them all?",
|
|
count);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
|
|
/* Translators: Note there are always more than 10 mails selected */
|
|
ask = ngettext ("You have selected %d mails to be converted to tasks. Do you really want to add them all?",
|
|
"You have selected %d mails to be converted to tasks. Do you really want to add them all?",
|
|
count);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
|
|
/* Translators: Note there are always more than 10 mails selected */
|
|
ask = ngettext ("You have selected %d mails to be converted to memos. Do you really want to add them all?",
|
|
"You have selected %d mails to be converted to memos. Do you really want to add them all?",
|
|
count);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
return ask;
|
|
}
|
|
|
|
static void
|
|
comp_editor_closed (CompEditor *editor,
|
|
gboolean accepted,
|
|
struct _manage_comp *mc)
|
|
{
|
|
if (!mc)
|
|
return;
|
|
|
|
if (!accepted && mc->mails_done < mc->mails_count)
|
|
mc->can_continue = (do_ask (_("Do you wish to continue converting remaining mails?"), FALSE) == GTK_RESPONSE_YES);
|
|
|
|
/* Signal the do_mail_to_event thread that editor was closed and editor
|
|
* for next event can be displayed (if any) */
|
|
g_cond_signal (mc->cond);
|
|
}
|
|
|
|
/*
|
|
* This handler takes title of the editor window and
|
|
* inserts information about number of processed mails and
|
|
* number of all mails to process, so the window title
|
|
* will look like "Appointment (3/10) - An appoitment name"
|
|
*/
|
|
static void
|
|
comp_editor_title_changed (GtkWidget *widget,
|
|
GParamSpec *pspec,
|
|
struct _manage_comp *mc)
|
|
{
|
|
GtkWindow *editor = GTK_WINDOW (widget);
|
|
const gchar *title = gtk_window_get_title (editor);
|
|
gchar *new_title;
|
|
gchar *splitter;
|
|
gchar *comp_name, *task_name;
|
|
|
|
if (!mc)
|
|
return;
|
|
|
|
/* Recursion prevence */
|
|
if (mc->editor_title && g_utf8_collate (mc->editor_title, title) == 0)
|
|
return;
|
|
|
|
splitter = strchr (title, '-');
|
|
if (!splitter)
|
|
return;
|
|
|
|
comp_name = g_strndup (title, splitter - title - 1);
|
|
task_name = g_strdup (splitter + 2);
|
|
new_title = g_strdup_printf ("%s (%d/%d) - %s",
|
|
comp_name, mc->mails_done, mc->mails_count, task_name);
|
|
|
|
/* Remember the new title, so that when gtk_window_set_title() causes
|
|
* this handler to be recursively called, we can recognize that and
|
|
* prevent endless recursion */
|
|
if (mc->editor_title)
|
|
g_free (mc->editor_title);
|
|
mc->editor_title = new_title;
|
|
|
|
gtk_window_set_title (editor, new_title);
|
|
|
|
g_free (comp_name);
|
|
g_free (task_name);
|
|
}
|
|
|
|
static gboolean
|
|
do_manage_comp_idle (struct _manage_comp *mc)
|
|
{
|
|
GError *error = NULL;
|
|
ECalClientSourceType source_type = E_CAL_CLIENT_SOURCE_TYPE_LAST;
|
|
ECalComponent *edit_comp = NULL;
|
|
|
|
g_return_val_if_fail (mc, FALSE);
|
|
|
|
source_type = e_cal_client_get_source_type (mc->client);
|
|
|
|
if (source_type == E_CAL_CLIENT_SOURCE_TYPE_LAST) {
|
|
free_manage_comp_struct (mc);
|
|
|
|
g_warning ("mail-to-task: Incorrect call of %s, no data given", G_STRFUNC);
|
|
return FALSE;
|
|
}
|
|
|
|
if (mc->stored_comp) {
|
|
const gchar *ask = get_question_edit_old (source_type);
|
|
|
|
if (ask) {
|
|
gchar *msg = g_strdup_printf (ask, icalcomponent_get_summary (mc->stored_comp) ? icalcomponent_get_summary (mc->stored_comp) : _("[No Summary]"));
|
|
gint chosen;
|
|
|
|
chosen = do_ask (msg, TRUE);
|
|
|
|
if (chosen == GTK_RESPONSE_YES) {
|
|
edit_comp = e_cal_component_new ();
|
|
if (!e_cal_component_set_icalcomponent (edit_comp, icalcomponent_new_clone (mc->stored_comp))) {
|
|
g_object_unref (edit_comp);
|
|
edit_comp = NULL;
|
|
error = g_error_new (E_CAL_CLIENT_ERROR,
|
|
E_CAL_CLIENT_ERROR_INVALID_OBJECT,
|
|
"%s", _("Invalid object returned from a server"));
|
|
|
|
}
|
|
} else if (chosen == GTK_RESPONSE_NO) {
|
|
/* user wants to create a new event, thus generate a new UID */
|
|
gchar *new_uid = e_cal_component_gen_uid ();
|
|
edit_comp = mc->comp;
|
|
e_cal_component_set_uid (edit_comp, new_uid);
|
|
e_cal_component_set_recurid (edit_comp, NULL);
|
|
g_free (new_uid);
|
|
}
|
|
g_free (msg);
|
|
}
|
|
} else {
|
|
edit_comp = mc->comp;
|
|
}
|
|
|
|
if (edit_comp) {
|
|
EShell *shell;
|
|
CompEditor *editor;
|
|
|
|
/* FIXME Pass in the EShell instance. */
|
|
shell = e_shell_get_default ();
|
|
editor = get_component_editor (shell, mc->client, edit_comp,
|
|
edit_comp == mc->comp, &error);
|
|
|
|
if (editor && !error) {
|
|
/* Force editor's title change */
|
|
comp_editor_title_changed (GTK_WIDGET (editor), NULL, mc);
|
|
|
|
g_signal_connect (
|
|
editor, "notify::title",
|
|
G_CALLBACK (comp_editor_title_changed), mc);
|
|
g_signal_connect (
|
|
editor, "comp_closed",
|
|
G_CALLBACK (comp_editor_closed), mc);
|
|
|
|
gtk_window_present (GTK_WINDOW (editor));
|
|
|
|
if (edit_comp != mc->comp)
|
|
g_object_unref (edit_comp);
|
|
} else {
|
|
g_warning ("Failed to create event editor: %s", error ? error->message : "Unknown error");
|
|
g_cond_signal (mc->cond);
|
|
}
|
|
} else {
|
|
/* User canceled editing already existing event, so treat it as if he just closed the editor window */
|
|
comp_editor_closed (NULL, FALSE, mc);
|
|
g_cond_signal (mc->cond);
|
|
}
|
|
|
|
if (error) {
|
|
e_notice (NULL, GTK_MESSAGE_ERROR, _("An error occurred during processing: %s"), error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
typedef struct {
|
|
ECalClient *client;
|
|
CamelFolder *folder;
|
|
GPtrArray *uids;
|
|
gchar *selected_text;
|
|
gboolean with_attendees;
|
|
}AsyncData;
|
|
|
|
static gboolean
|
|
do_mail_to_event (AsyncData *data)
|
|
{
|
|
ECalClient *client = data->client;
|
|
CamelFolder *folder = data->folder;
|
|
GPtrArray *uids = data->uids;
|
|
GError *err = NULL;
|
|
|
|
/* open the task client */
|
|
if (!e_client_open_sync (E_CLIENT (client), FALSE, NULL, &err)) {
|
|
report_error_idle (_("Cannot open calendar. %s"), err ? err->message : _("Unknown error."));
|
|
} else if (e_client_is_readonly (E_CLIENT (client))) {
|
|
if (err)
|
|
report_error_idle ("Check readonly failed. %s", err->message);
|
|
else {
|
|
switch (e_cal_client_get_source_type (client)) {
|
|
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
|
|
report_error_idle (_("Selected source is read only, thus cannot create event there. Select other source, please."), NULL);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
|
|
report_error_idle (_("Selected source is read only, thus cannot create task there. Select other source, please."), NULL);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
|
|
report_error_idle (_("Selected source is read only, thus cannot create memo there. Select other source, please."), NULL);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
gint i;
|
|
ECalClientSourceType source_type = e_cal_client_get_source_type (client);
|
|
ECalComponentDateTime dt, dt2;
|
|
struct icaltimetype tt, tt2;
|
|
struct _manage_comp *oldmc = NULL;
|
|
|
|
#define cache_backend_prop(prop) { \
|
|
gchar *val = NULL; \
|
|
e_client_get_backend_property_sync (E_CLIENT (client), prop, &val, NULL, NULL); \
|
|
g_free (val); \
|
|
}
|
|
|
|
/* precache backend properties, thus editor have them ready when needed */
|
|
cache_backend_prop (CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS);
|
|
cache_backend_prop (CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS);
|
|
cache_backend_prop (CAL_BACKEND_PROPERTY_DEFAULT_OBJECT);
|
|
e_client_get_capabilities (E_CLIENT (client));
|
|
|
|
#undef cache_backend_prop
|
|
|
|
/* set start day of the event as today, without time - easier than looking for a calendar's time zone */
|
|
tt = icaltime_today ();
|
|
dt.value = &tt;
|
|
dt.tzid = NULL;
|
|
|
|
tt2 = tt;
|
|
icaltime_adjust (&tt2, 1, 0, 0, 0);
|
|
dt2.value = &tt2;
|
|
dt2.tzid = NULL;
|
|
|
|
for (i = 0; i < (uids ? uids->len : 0); i++) {
|
|
CamelMimeMessage *message;
|
|
ECalComponent *comp;
|
|
ECalComponentText text;
|
|
icalproperty *icalprop;
|
|
icalcomponent *icalcomp;
|
|
struct _manage_comp *mc;
|
|
|
|
/* retrieve the message from the CamelFolder */
|
|
/* FIXME Not passing a GCancellable or GError. */
|
|
message = camel_folder_get_message_sync (
|
|
folder, g_ptr_array_index (uids, i),
|
|
NULL, NULL);
|
|
if (!message) {
|
|
continue;
|
|
}
|
|
|
|
comp = e_cal_component_new ();
|
|
|
|
switch (source_type) {
|
|
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
|
|
e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
|
|
e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
|
|
e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
e_cal_component_set_uid (comp, camel_mime_message_get_message_id (message));
|
|
e_cal_component_set_dtstart (comp, &dt);
|
|
|
|
if (source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
|
|
/* make it an all-day event */
|
|
e_cal_component_set_dtend (comp, &dt2);
|
|
}
|
|
|
|
/* set the summary */
|
|
text.value = camel_mime_message_get_subject (message);
|
|
text.altrep = NULL;
|
|
e_cal_component_set_summary (comp, &text);
|
|
|
|
/* set all fields */
|
|
if (data->selected_text) {
|
|
GSList sl;
|
|
|
|
text.value = data->selected_text;
|
|
text.altrep = NULL;
|
|
sl.next = NULL;
|
|
sl.data = &text;
|
|
|
|
e_cal_component_set_description_list (comp, &sl);
|
|
} else
|
|
set_description (comp, message);
|
|
|
|
if (data->with_attendees) {
|
|
gchar *organizer;
|
|
|
|
/* set actual user as organizer, to be able to change event's properties */
|
|
organizer = set_organizer (comp, data->folder);
|
|
set_attendees (comp, message, organizer);
|
|
g_free (organizer);
|
|
}
|
|
|
|
/* set attachment files */
|
|
set_attachments (client, comp, message);
|
|
|
|
/* priority */
|
|
set_priority (comp, CAMEL_MIME_PART (message));
|
|
|
|
/* no need to increment a sequence number, this is a new component */
|
|
e_cal_component_abort_sequence (comp);
|
|
|
|
icalcomp = e_cal_component_get_icalcomponent (comp);
|
|
|
|
icalprop = icalproperty_new_x ("1");
|
|
icalproperty_set_x_name (icalprop, "X-EVOLUTION-MOVE-CALENDAR");
|
|
icalcomponent_add_property (icalcomp, icalprop);
|
|
|
|
mc = g_new0 (struct _manage_comp, 1);
|
|
mc->client = g_object_ref (client);
|
|
mc->comp = g_object_ref (comp);
|
|
mc->mutex = g_mutex_new ();
|
|
mc->cond = g_cond_new ();
|
|
mc->mails_count = uids->len;
|
|
mc->mails_done = i + 1; /* Current task */
|
|
mc->editor_title = NULL;
|
|
mc->can_continue = TRUE;
|
|
|
|
if (oldmc) {
|
|
/* Wait for user to quit the editor created in previous iteration
|
|
* before displaying next one */
|
|
gboolean can_continue;
|
|
g_mutex_lock (oldmc->mutex);
|
|
g_cond_wait (oldmc->cond, oldmc->mutex);
|
|
g_mutex_unlock (oldmc->mutex);
|
|
can_continue = oldmc->can_continue;
|
|
free_manage_comp_struct (oldmc);
|
|
oldmc = NULL;
|
|
|
|
if (!can_continue)
|
|
break;
|
|
}
|
|
|
|
if (!e_cal_client_get_object_sync (client, icalcomponent_get_uid (icalcomp), NULL, &(mc->stored_comp), NULL, NULL))
|
|
mc->stored_comp = NULL;
|
|
|
|
g_idle_add ((GSourceFunc) do_manage_comp_idle, mc);
|
|
|
|
oldmc = mc;
|
|
|
|
g_object_unref (comp);
|
|
g_object_unref (message);
|
|
|
|
}
|
|
|
|
/* Wait for the last editor and then clean up */
|
|
if (oldmc) {
|
|
g_mutex_lock (oldmc->mutex);
|
|
g_cond_wait (oldmc->cond, oldmc->mutex);
|
|
g_mutex_unlock (oldmc->mutex);
|
|
free_manage_comp_struct (oldmc);
|
|
}
|
|
}
|
|
|
|
/* free memory */
|
|
g_object_unref (data->client);
|
|
em_utils_uids_free (uids);
|
|
g_object_unref (folder);
|
|
g_free (data->selected_text);
|
|
g_free (data);
|
|
data = NULL;
|
|
|
|
if (err)
|
|
g_error_free (err);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
text_contains_nonwhitespace (const gchar *text,
|
|
gint len)
|
|
{
|
|
const gchar *p;
|
|
gunichar c = 0;
|
|
|
|
if (!text || len <= 0)
|
|
return FALSE;
|
|
|
|
p = text;
|
|
|
|
while (p && p - text < len) {
|
|
c = g_utf8_get_char (p);
|
|
if (!c)
|
|
break;
|
|
|
|
if (!g_unichar_isspace (c))
|
|
break;
|
|
|
|
p = g_utf8_next_char (p);
|
|
}
|
|
|
|
return p - text < len - 1 && c != 0;
|
|
}
|
|
|
|
/* should be freed with g_free after done with it */
|
|
static gchar *
|
|
get_selected_text (EMailReader *reader)
|
|
{
|
|
EMailDisplay *display;
|
|
gchar *text = NULL;
|
|
|
|
display = e_mail_reader_get_mail_display (reader);
|
|
|
|
if (!e_web_view_is_selection_active (E_WEB_VIEW (display)))
|
|
return NULL;
|
|
|
|
text = e_mail_display_get_selection_plain_text (display);
|
|
|
|
if (text == NULL || !text_contains_nonwhitespace (text, strlen (text))) {
|
|
g_free (text);
|
|
return NULL;
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
static void
|
|
mail_to_event (ECalClientSourceType source_type,
|
|
gboolean with_attendees,
|
|
EMailReader *reader)
|
|
{
|
|
EShell *shell;
|
|
EMailBackend *backend;
|
|
ESourceRegistry *registry;
|
|
CamelFolder *folder;
|
|
GPtrArray *uids;
|
|
ESource *source = NULL;
|
|
ESource *default_source;
|
|
GList *list, *iter;
|
|
GtkWindow *parent;
|
|
const gchar *extension_name;
|
|
GError *error = NULL;
|
|
|
|
folder = e_mail_reader_get_folder (reader);
|
|
parent = e_mail_reader_get_window (reader);
|
|
uids = e_mail_reader_get_selected_uids (reader);
|
|
|
|
/* Ask before converting 10 or more mails to events. */
|
|
if (uids->len > 10) {
|
|
gchar *question;
|
|
gint response;
|
|
|
|
question = g_strdup_printf (
|
|
get_question_add_all_mails (source_type, uids->len), uids->len);
|
|
response = do_ask (question, FALSE);
|
|
g_free (question);
|
|
|
|
if (response == GTK_RESPONSE_NO) {
|
|
em_utils_uids_free (uids);
|
|
g_object_unref (folder);
|
|
return;
|
|
}
|
|
}
|
|
|
|
backend = e_mail_reader_get_backend (reader);
|
|
shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend));
|
|
registry = e_shell_get_registry (shell);
|
|
|
|
switch (source_type) {
|
|
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
|
|
extension_name = E_SOURCE_EXTENSION_CALENDAR;
|
|
default_source = e_source_registry_ref_default_calendar (registry);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
|
|
extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
|
|
default_source = e_source_registry_ref_default_memo_list (registry);
|
|
break;
|
|
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
|
|
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
|
|
default_source = e_source_registry_ref_default_task_list (registry);
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
list = e_source_registry_list_sources (registry, extension_name);
|
|
|
|
/* If there is only one writable source, no need to prompt the user. */
|
|
for (iter = list; iter != NULL; iter = g_list_next (iter)) {
|
|
ESource *candidate = E_SOURCE (iter->data);
|
|
|
|
if (e_source_get_writable (candidate)) {
|
|
if (source == NULL)
|
|
source = candidate;
|
|
else {
|
|
source = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_list_free_full (list, (GDestroyNotify) g_object_unref);
|
|
|
|
if (source == NULL) {
|
|
GtkWidget *dialog;
|
|
ESourceSelector *selector;
|
|
|
|
/* ask the user which tasks list to save to */
|
|
dialog = e_source_selector_dialog_new (
|
|
parent, registry, extension_name);
|
|
|
|
selector = e_source_selector_dialog_get_selector (
|
|
E_SOURCE_SELECTOR_DIALOG (dialog));
|
|
|
|
e_source_selector_set_primary_selection (
|
|
selector, default_source);
|
|
|
|
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
|
|
source = e_source_selector_dialog_peek_primary_selection (
|
|
E_SOURCE_SELECTOR_DIALOG (dialog));
|
|
|
|
gtk_widget_destroy (dialog);
|
|
} else if (!source && default_source) {
|
|
source = default_source;
|
|
} else if (!source) {
|
|
e_notice (NULL, GTK_MESSAGE_ERROR, _("No writable calendar is available."));
|
|
|
|
em_utils_uids_free (uids);
|
|
g_object_unref (folder);
|
|
if (error)
|
|
g_error_free (error);
|
|
goto exit;
|
|
}
|
|
|
|
if (source) {
|
|
/* if a source has been selected, perform the mail2event operation */
|
|
ECalClient *client = NULL;
|
|
AsyncData *data = NULL;
|
|
GThread *thread = NULL;
|
|
GError *error = NULL;
|
|
|
|
client = e_cal_client_new (source, source_type, &error);
|
|
if (!client) {
|
|
e_notice (
|
|
parent, GTK_MESSAGE_ERROR,
|
|
"Could not connect to '%s'",
|
|
e_source_get_display_name (source));
|
|
goto exit;
|
|
}
|
|
|
|
/* Fill the elements in AsynData */
|
|
data = g_new0 (AsyncData, 1);
|
|
data->client = client;
|
|
data->folder = folder;
|
|
data->uids = uids;
|
|
data->with_attendees = with_attendees;
|
|
|
|
if (uids->len == 1)
|
|
data->selected_text = get_selected_text (reader);
|
|
else
|
|
data->selected_text = NULL;
|
|
|
|
thread = g_thread_create ((GThreadFunc) do_mail_to_event, data, FALSE, &error);
|
|
if (!thread) {
|
|
g_warning (G_STRLOC ": %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
g_object_unref (default_source);
|
|
}
|
|
|
|
static void
|
|
action_mail_convert_to_event_cb (GtkAction *action,
|
|
EMailReader *reader)
|
|
{
|
|
mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, FALSE, reader);
|
|
}
|
|
|
|
static void
|
|
action_mail_convert_to_meeting_cb (GtkAction *action,
|
|
EMailReader *reader)
|
|
{
|
|
mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_EVENTS, TRUE, reader);
|
|
}
|
|
|
|
static void
|
|
action_mail_convert_to_memo_cb (GtkAction *action,
|
|
EMailReader *reader)
|
|
{
|
|
mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_MEMOS, FALSE, reader);
|
|
}
|
|
|
|
static void
|
|
action_mail_convert_to_task_cb (GtkAction *action,
|
|
EMailReader *reader)
|
|
{
|
|
mail_to_event (E_CAL_CLIENT_SOURCE_TYPE_TASKS, FALSE, reader);
|
|
}
|
|
|
|
/* Note, we're not using EPopupActions here because we update the state
|
|
* of entire actions groups instead of individual actions. EPopupActions
|
|
* just proxy the state of individual actions. */
|
|
|
|
static GtkActionEntry multi_selection_entries[] = {
|
|
|
|
{ "mail-convert-to-appointment",
|
|
"appointment-new",
|
|
N_("Create an _Appointment"),
|
|
NULL,
|
|
N_("Create a new event from the selected message"),
|
|
G_CALLBACK (action_mail_convert_to_event_cb) },
|
|
|
|
{ "mail-convert-to-memo",
|
|
"stock_insert-note",
|
|
N_("Create a Mem_o"),
|
|
NULL,
|
|
N_("Create a new memo from the selected message"),
|
|
G_CALLBACK (action_mail_convert_to_memo_cb) },
|
|
|
|
{ "mail-convert-to-task",
|
|
"stock_todo",
|
|
N_("Create a _Task"),
|
|
NULL,
|
|
N_("Create a new task from the selected message"),
|
|
G_CALLBACK (action_mail_convert_to_task_cb) }
|
|
};
|
|
|
|
static GtkActionEntry single_selection_entries[] = {
|
|
|
|
{ "mail-convert-to-meeting",
|
|
"stock_new-meeting",
|
|
N_("Create a _Meeting"),
|
|
NULL,
|
|
N_("Create a new meeting from the selected message"),
|
|
G_CALLBACK (action_mail_convert_to_meeting_cb) }
|
|
};
|
|
|
|
static void
|
|
update_actions_any_cb (EMailReader *reader,
|
|
guint32 state,
|
|
GtkActionGroup *action_group)
|
|
{
|
|
gboolean sensitive;
|
|
|
|
sensitive =
|
|
(state & E_MAIL_READER_SELECTION_SINGLE) ||
|
|
(state & E_MAIL_READER_SELECTION_MULTIPLE);
|
|
|
|
gtk_action_group_set_sensitive (action_group, sensitive);
|
|
}
|
|
|
|
static void
|
|
update_actions_one_cb (EMailReader *reader,
|
|
guint32 state,
|
|
GtkActionGroup *action_group)
|
|
{
|
|
gboolean sensitive;
|
|
|
|
sensitive = (state & E_MAIL_READER_SELECTION_SINGLE);
|
|
|
|
gtk_action_group_set_sensitive (action_group, sensitive);
|
|
}
|
|
|
|
static void
|
|
setup_actions (EMailReader *reader,
|
|
GtkUIManager *ui_manager)
|
|
{
|
|
GtkActionGroup *action_group;
|
|
const gchar *domain = GETTEXT_PACKAGE;
|
|
|
|
action_group = gtk_action_group_new ("mail-convert-any");
|
|
gtk_action_group_set_translation_domain (action_group, domain);
|
|
gtk_action_group_add_actions (
|
|
action_group, multi_selection_entries,
|
|
G_N_ELEMENTS (multi_selection_entries), reader);
|
|
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
|
|
g_object_unref (action_group);
|
|
|
|
/* GtkUIManager now owns the action group reference.
|
|
* The signal we're connecting to will only be emitted
|
|
* during the GtkUIManager's lifetime, so the action
|
|
* group will not disappear on us. */
|
|
|
|
g_signal_connect (
|
|
reader, "update-actions",
|
|
G_CALLBACK (update_actions_any_cb), action_group);
|
|
|
|
action_group = gtk_action_group_new ("mail-convert-one");
|
|
gtk_action_group_set_translation_domain (action_group, domain);
|
|
gtk_action_group_add_actions (
|
|
action_group, single_selection_entries,
|
|
G_N_ELEMENTS (single_selection_entries), reader);
|
|
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
|
|
g_object_unref (action_group);
|
|
|
|
/* GtkUIManager now owns the action group reference.
|
|
* The signal we're connecting to will only be emitted
|
|
* during the GtkUIManager's lifetime, so the action
|
|
* group will not disappear on us. */
|
|
|
|
g_signal_connect (
|
|
reader, "update-actions",
|
|
G_CALLBACK (update_actions_one_cb), action_group);
|
|
}
|
|
|
|
gboolean
|
|
mail_browser_init (GtkUIManager *ui_manager,
|
|
EMailBrowser *browser)
|
|
{
|
|
setup_actions (E_MAIL_READER (browser), ui_manager);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
mail_shell_view_init (GtkUIManager *ui_manager,
|
|
EShellView *shell_view)
|
|
{
|
|
EShellContent *shell_content;
|
|
|
|
shell_content = e_shell_view_get_shell_content (shell_view);
|
|
|
|
setup_actions (E_MAIL_READER (shell_content), ui_manager);
|
|
|
|
return TRUE;
|
|
}
|