[evolution] Bug 773548 - Be able to convert composed mail to meeting request
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Bug 773548 - Be able to convert composed mail to meeting request
- Date: Wed, 14 Dec 2016 10:21:33 +0000 (UTC)
commit 8e23d26219b80a416c30240bdf014873e654eb4d
Author: Milan Crha <mcrha redhat com>
Date: Wed Dec 14 11:20:03 2016 +0100
Bug 773548 - Be able to convert composed mail to meeting request
po/POTFILES.in | 2 +
src/calendar/gui/e-comp-editor.c | 2 +
src/composer/evolution-composer.ui | 1 +
src/composer/mail-composer.error.xml | 28 ++
src/e-util/e-misc-utils.c | 14 +-
src/modules/CMakeLists.txt | 1 +
src/modules/composer-to-meeting/CMakeLists.txt | 24 ++
.../composer-to-meeting/composer-to-meeting.c | 39 ++
.../composer-to-meeting/e-composer-to-meeting.c | 389 ++++++++++++++++++
.../composer-to-meeting/e-composer-to-meeting.h | 28 ++
.../composer-to-meeting/e-meeting-to-composer.c | 425 ++++++++++++++++++++
.../composer-to-meeting/e-meeting-to-composer.h | 28 ++
12 files changed, 975 insertions(+), 6 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a907a98..4019a48 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -430,6 +430,8 @@ src/modules/calendar/e-task-shell-migrate.c
src/modules/calendar/e-task-shell-view-actions.c
src/modules/calendar/e-task-shell-view.c
src/modules/calendar/e-task-shell-view-private.c
+src/modules/composer-to-meeting/e-composer-to-meeting.c
+src/modules/composer-to-meeting/e-meeting-to-composer.c
src/modules/itip-formatter/e-mail-formatter-itip.c
src/modules/itip-formatter/itip-view.c
src/modules/itip-formatter/org-gnome-itip-formatter.error.xml
diff --git a/src/calendar/gui/e-comp-editor.c b/src/calendar/gui/e-comp-editor.c
index 3c2d570..712413b 100644
--- a/src/calendar/gui/e-comp-editor.c
+++ b/src/calendar/gui/e-comp-editor.c
@@ -1924,6 +1924,8 @@ e_comp_editor_constructed (GObject *object)
" <menuitem action='save'/>"
" <menuitem action='save-and-close'/>"
" <separator/>"
+ " <placeholder name='custom-actions-placeholder'/>"
+ " <separator/>"
" <menuitem action='print-preview'/>"
" <menuitem action='print'/>"
" <separator/>"
diff --git a/src/composer/evolution-composer.ui b/src/composer/evolution-composer.ui
index b74bb30..c5aada0 100644
--- a/src/composer/evolution-composer.ui
+++ b/src/composer/evolution-composer.ui
@@ -5,6 +5,7 @@
<menuitem action='send'/>
<separator/>
<menuitem action='new-message'/>
+ <placeholder name='custom-actions-placeholder'/>
<separator/>
<menuitem action='save'/>
<menuitem action='save-as'/>
diff --git a/src/composer/mail-composer.error.xml b/src/composer/mail-composer.error.xml
index 01b6513..b6943fc 100644
--- a/src/composer/mail-composer.error.xml
+++ b/src/composer/mail-composer.error.xml
@@ -107,4 +107,32 @@
<_secondary>The reported error was “{0}”. The message has most likely not been saved.</_secondary>
</error>
+ <error id="prompt-composer-to-meeting" type="question" default="GTK_RESPONSE_YES">
+ <_primary>Are you sure you want to convert the message into a meeting?</_primary>
+ <_secondary xml:space="preserve">By converting the message into the meeting the composed message will be
closed and the changes being done discarded.</_secondary>
+ <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+ <button _label="Convert to _Meeting" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="prompt-event-to-composer" type="question" default="GTK_RESPONSE_YES">
+ <_primary>Are you sure you want to convert the event into a message?</_primary>
+ <_secondary xml:space="preserve">By converting the event into the message the editing window will be
closed and the changes being done discarded.</_secondary>
+ <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+ <button _label="Convert to _Message" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="prompt-memo-to-composer" type="question" default="GTK_RESPONSE_YES">
+ <_primary>Are you sure you want to convert the memo into a message?</_primary>
+ <_secondary xml:space="preserve">By converting the memo into the message the editing window will be closed
and the changes being done discarded.</_secondary>
+ <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+ <button _label="Convert to _Message" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="prompt-task-to-composer" type="question" default="GTK_RESPONSE_YES">
+ <_primary>Are you sure you want to convert the task into a message?</_primary>
+ <_secondary xml:space="preserve">By converting the task into the message the editing window will be closed
and the changes being done discarded.</_secondary>
+ <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+ <button _label="Convert to _Message" response="GTK_RESPONSE_YES"/>
+ </error>
+
</error-list>
diff --git a/src/e-util/e-misc-utils.c b/src/e-util/e-misc-utils.c
index eb60356..11edab6 100644
--- a/src/e-util/e-misc-utils.c
+++ b/src/e-util/e-misc-utils.c
@@ -3264,14 +3264,16 @@ e_util_prompt_user (GtkWindow *parent,
GtkWidget *container;
va_list ap;
gint button;
- GSettings *settings;
+ GSettings *settings = NULL;
EAlert *alert = NULL;
- settings = e_util_ref_settings (settings_schema);
+ if (promptkey) {
+ settings = e_util_ref_settings (settings_schema);
- if (promptkey && !g_settings_get_boolean (settings, promptkey)) {
- g_object_unref (settings);
- return TRUE;
+ if (!g_settings_get_boolean (settings, promptkey)) {
+ g_object_unref (settings);
+ return TRUE;
+ }
}
va_start (ap, tag);
@@ -3300,7 +3302,7 @@ e_util_prompt_user (GtkWindow *parent,
gtk_widget_destroy (dialog);
- g_object_unref (settings);
+ g_clear_object (&settings);
return button == GTK_RESPONSE_YES;
}
diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt
index 46ecd74..b554ec0 100644
--- a/src/modules/CMakeLists.txt
+++ b/src/modules/CMakeLists.txt
@@ -100,6 +100,7 @@ add_subdirectory(cal-config-google)
add_subdirectory(cal-config-local)
add_subdirectory(cal-config-webcal)
add_subdirectory(composer-autosave)
+add_subdirectory(composer-to-meeting)
add_subdirectory(contact-photos)
add_subdirectory(gravatar)
add_subdirectory(itip-formatter)
diff --git a/src/modules/composer-to-meeting/CMakeLists.txt b/src/modules/composer-to-meeting/CMakeLists.txt
new file mode 100644
index 0000000..3e9c8bd
--- /dev/null
+++ b/src/modules/composer-to-meeting/CMakeLists.txt
@@ -0,0 +1,24 @@
+set(extra_deps
+ evolution-mail-composer
+ evolution-calendar
+)
+set(sources
+ composer-to-meeting.c
+ e-composer-to-meeting.c
+ e-composer-to-meeting.h
+ e-meeting-to-composer.c
+ e-meeting-to-composer.h
+)
+set(extra_defines)
+set(extra_cflags)
+set(extra_incdirs)
+set(extra_ldflags)
+
+add_evolution_module(module-composer-to-meeting
+ sources
+ extra_deps
+ extra_defines
+ extra_cflags
+ extra_incdirs
+ extra_ldflags
+)
diff --git a/src/modules/composer-to-meeting/composer-to-meeting.c
b/src/modules/composer-to-meeting/composer-to-meeting.c
new file mode 100644
index 0000000..1217010
--- /dev/null
+++ b/src/modules/composer-to-meeting/composer-to-meeting.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <gmodule.h>
+#include <glib-object.h>
+
+#include "e-composer-to-meeting.h"
+#include "e-meeting-to-composer.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_composer_to_meeting_type_register (type_module);
+ e_meeting_to_composer_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/src/modules/composer-to-meeting/e-composer-to-meeting.c
b/src/modules/composer-to-meeting/e-composer-to-meeting.c
new file mode 100644
index 0000000..cda743d
--- /dev/null
+++ b/src/modules/composer-to-meeting/e-composer-to-meeting.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-util/e-util.h"
+#include "composer/e-msg-composer.h"
+#include "composer/e-composer-from-header.h"
+#include "calendar/gui/e-comp-editor.h"
+#include "calendar/gui/e-comp-editor-page-attachments.h"
+
+#include "e-composer-to-meeting.h"
+
+/* Standard GObject macros */
+#define E_TYPE_COMPOSER_TO_MEETING \
+ (e_composer_to_meeting_get_type ())
+#define E_COMPOSER_TO_MEETING(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_COMPOSER_TO_MEETING, EComposerToMeeting))
+#define E_COMPOSER_TO_MEETING_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_COMPOSER_TO_MEETING, EComposerToMeetingClass))
+#define E_IS_COMPOSER_TO_MEETING(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_COMPOSER_TO_MEETING))
+#define E_IS_COMPOSER_TO_MEETING_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_COMPOSER_TO_MEETING))
+#define E_COMPOSER_TO_MEETING_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_COMPOSER_TO_MEETING, EComposerToMeetingClass))
+
+typedef struct _EComposerToMeeting EComposerToMeeting;
+typedef struct _EComposerToMeetingClass EComposerToMeetingClass;
+
+struct _EComposerToMeeting {
+ EExtension parent;
+};
+
+struct _EComposerToMeetingClass {
+ EExtensionClass parent_class;
+};
+
+GType e_composer_to_meeting_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_DYNAMIC_TYPE (EComposerToMeeting, e_composer_to_meeting, E_TYPE_EXTENSION)
+
+static void
+composer_to_meeting_attendees_free (gpointer ptr)
+{
+ ECalComponentAttendee *attendee = ptr;
+
+ if (attendee) {
+ g_free ((gpointer) attendee->value);
+ g_free ((gpointer) attendee->cn);
+ g_free (attendee);
+ }
+}
+
+static ECalComponent *
+composer_to_meeting_component (EMsgComposer *composer)
+{
+ ECalComponent *comp;
+ EHTMLEditor *html_editor;
+ EContentEditor *cnt_editor;
+ EComposerHeaderTable *header_table;
+ EDestination **destinations_array[3];
+ ESource *source;
+ gchar *alias_name = NULL, *alias_address = NULL, *uid, *text;
+ GSList *attendees = NULL;
+ const gchar *subject;
+ gint ii;
+
+ g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+ comp = e_cal_component_new_from_icalcomponent (e_cal_util_new_component (ICAL_VEVENT_COMPONENT));
+ g_return_val_if_fail (comp != NULL, NULL);
+
+ header_table = e_msg_composer_get_header_table (composer);
+
+ /* Summary */
+ subject = e_composer_header_table_get_subject (header_table);
+ if (subject && *subject) {
+ ECalComponentText summary;
+
+ summary.value = subject;
+ summary.altrep = NULL;
+
+ e_cal_component_set_summary (comp, &summary);
+ }
+
+ /* Organizer */
+ uid = e_composer_header_table_dup_identity_uid (header_table, &alias_name, &alias_address);
+ source = e_composer_header_table_ref_source (header_table, uid);
+ if (source) {
+ EComposerHeader *composer_header;
+ const gchar *name = NULL, *address = NULL;
+
+ composer_header = e_composer_header_table_get_header (header_table, E_COMPOSER_HEADER_FROM);
+ if (e_composer_from_header_get_override_visible (E_COMPOSER_FROM_HEADER (composer_header))) {
+ name = e_composer_header_table_get_from_name (header_table);
+ address = e_composer_header_table_get_from_address (header_table);
+
+ if (address && !*address)
+ address = NULL;
+ }
+
+ if (!address) {
+ if (alias_name)
+ name = alias_name;
+ if (alias_address)
+ address = alias_address;
+ }
+
+ if (!address || !name || !*name) {
+ ESourceMailIdentity *mail_identity;
+
+ mail_identity = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
+
+ if (!name || !*name)
+ name = e_source_mail_identity_get_name (mail_identity);
+
+ if (!address)
+ address = e_source_mail_identity_get_address (mail_identity);
+ }
+
+ if (address && *address) {
+ ECalComponentOrganizer organizer = {NULL, NULL, NULL, NULL};
+ gchar *mailto;
+
+ mailto = g_strconcat ("mailto:", address, NULL);
+ organizer.value = mailto;
+ organizer.cn = name;
+
+ e_cal_component_set_organizer (comp, &organizer);
+
+ g_free (mailto);
+ }
+
+ g_object_unref (source);
+ g_free (alias_address);
+ g_free (alias_name);
+ g_free (uid);
+ }
+
+ /* Attendees */
+ destinations_array[0] = e_composer_header_table_get_destinations_to (header_table);
+ destinations_array[1] = e_composer_header_table_get_destinations_cc (header_table);
+ destinations_array[2] = e_composer_header_table_get_destinations_bcc (header_table);
+ for (ii = 0; ii < 3; ii++) {
+ EDestination **destinations = destinations_array[ii];
+ CamelInternetAddress *address;
+ gchar *textrep;
+
+ if (!destinations)
+ continue;
+
+ textrep = e_destination_get_textrepv (destinations);
+ address = camel_internet_address_new ();
+
+ if (textrep) {
+ gint jj, len;
+
+ len = camel_address_decode (CAMEL_ADDRESS (address), textrep);
+ for (jj = 0; jj < len; jj++) {
+ const gchar *name = NULL, *mail = NULL;
+
+ if (camel_internet_address_get (address, jj, &name, &mail)) {
+ ECalComponentAttendee *attendee;
+
+ attendee = g_new0 (ECalComponentAttendee, 1);
+ attendee->value = g_strconcat ("mailto:", mail, NULL);
+ attendee->cn = g_strdup (name);
+ attendee->cutype = ICAL_CUTYPE_INDIVIDUAL;
+ attendee->status = ICAL_PARTSTAT_NEEDSACTION;
+ attendee->role = ii == 0 ? ICAL_ROLE_REQPARTICIPANT :
ICAL_ROLE_OPTPARTICIPANT;
+
+ attendees = g_slist_append (attendees, attendee);
+ }
+ }
+ }
+
+ g_free (textrep);
+ g_object_unref (address);
+ e_destination_freev (destinations);
+ }
+
+ e_cal_component_set_attendee_list (comp, attendees);
+
+ g_slist_free_full (attendees, composer_to_meeting_attendees_free);
+
+ /* Description */
+ html_editor = e_msg_composer_get_editor (composer);
+ cnt_editor = e_html_editor_get_content_editor (html_editor);
+ text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED |
E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL);
+ if (text && *text) {
+ ECalComponentText *description;
+ GSList *descr_list = NULL;
+
+ description = g_new0 (ECalComponentText, 1);
+ description->value = text;
+ description->altrep = NULL;
+
+ descr_list = g_slist_append (descr_list, description);
+
+ e_cal_component_set_description_list (comp, descr_list);
+
+ g_slist_free_full (descr_list, g_free);
+ }
+ g_free (text);
+
+ return comp;
+}
+
+static void
+composer_to_meeting_copy_attachments (EMsgComposer *composer,
+ ECompEditor *comp_editor)
+{
+ ECompEditorPage *page_attachments;
+ EAttachmentView *attachment_view;
+ EAttachmentStore *store;
+ GList *attachments, *link;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+ attachment_view = e_msg_composer_get_attachment_view (composer);
+ store = e_attachment_view_get_store (attachment_view);
+ attachments = e_attachment_store_get_attachments (store);
+
+ if (!attachments)
+ return;
+
+ page_attachments = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS);
+ if (page_attachments) {
+ store = e_comp_editor_page_attachments_get_store (E_COMP_EDITOR_PAGE_ATTACHMENTS
(page_attachments));
+
+ for (link = attachments; link; link = g_list_next (link)) {
+ EAttachment *attachment = link->data;
+
+ e_attachment_store_add_attachment (store, attachment);
+ }
+ }
+
+ g_list_free_full (attachments, g_object_unref);
+}
+
+static void
+action_composer_to_meeting_cb (GtkAction *action,
+ EMsgComposer *composer)
+{
+ ECalComponent *comp;
+ ECompEditor *comp_editor;
+ ECompEditorFlags flags;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+ if (!e_util_prompt_user (GTK_WINDOW (composer), NULL, NULL,
"mail-composer:prompt-composer-to-meeting", NULL))
+ return;
+
+ comp = composer_to_meeting_component (composer);
+ if (!comp)
+ return;
+
+ flags = E_COMP_EDITOR_FLAG_IS_NEW |
+ E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER |
+ E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
+
+ comp_editor = e_comp_editor_open_for_component (NULL, e_msg_composer_get_shell (composer),
+ NULL, e_cal_component_get_icalcomponent (comp), flags);
+
+ /* Attachments */
+ composer_to_meeting_copy_attachments (composer, comp_editor);
+
+ gtk_window_present (GTK_WINDOW (comp_editor));
+
+ g_object_unref (comp);
+
+ gtk_widget_destroy (GTK_WIDGET (composer));
+}
+
+static void
+e_composer_to_meeting_setup_ui (EMsgComposer *composer)
+{
+ const gchar *ui =
+ "<ui>"
+ " <menubar action='main-menu'>"
+ " <placeholder name='pre-edit-menu'>"
+ " <menu action='file-menu'>"
+ " <placeholder name='custom-actions-placeholder'>"
+ " <menuitem action='composer-to-meeting-action'/>"
+ " </placeholder>"
+ " </menu>"
+ " </placeholder>"
+ " </menubar>"
+ "</ui>";
+
+ GtkActionEntry entries[] = {
+ { "composer-to-meeting-action",
+ "stock_people",
+ N_("Convert to M_eeting"),
+ NULL,
+ N_("Convert the message to a meeting request"),
+ G_CALLBACK (action_composer_to_meeting_cb) }
+ };
+
+ EHTMLEditor *editor;
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+ editor = e_msg_composer_get_editor (composer);
+ ui_manager = e_html_editor_get_ui_manager (editor);
+ action_group = e_html_editor_get_action_group (editor, "composer");
+
+ gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), composer);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+ if (error) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+composer_to_meeting_constructed (GObject *object)
+{
+ EMsgComposer *composer;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_composer_to_meeting_parent_class)->constructed (object);
+
+ composer = E_MSG_COMPOSER (e_extension_get_extensible (E_EXTENSION (object)));
+
+ e_composer_to_meeting_setup_ui (composer);
+}
+
+static void
+e_composer_to_meeting_class_init (EComposerToMeetingClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = composer_to_meeting_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_MSG_COMPOSER;
+}
+
+static void
+e_composer_to_meeting_class_finalize (EComposerToMeetingClass *class)
+{
+}
+
+static void
+e_composer_to_meeting_init (EComposerToMeeting *extension)
+{
+}
+
+void
+e_composer_to_meeting_type_register (GTypeModule *type_module)
+{
+ /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+ * function, so we have to wrap it with a public function in
+ * order to register types from a separate compilation unit. */
+ e_composer_to_meeting_register_type (type_module);
+}
diff --git a/src/modules/composer-to-meeting/e-composer-to-meeting.h
b/src/modules/composer-to-meeting/e-composer-to-meeting.h
new file mode 100644
index 0000000..b69dc13
--- /dev/null
+++ b/src/modules/composer-to-meeting/e-composer-to-meeting.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_COMPOSER_TO_MEETING_H
+#define E_COMPOSER_TO_MEETING_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void e_composer_to_meeting_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_COMPOSER_TO_MEETING_H */
diff --git a/src/modules/composer-to-meeting/e-meeting-to-composer.c
b/src/modules/composer-to-meeting/e-meeting-to-composer.c
new file mode 100644
index 0000000..f0fec00
--- /dev/null
+++ b/src/modules/composer-to-meeting/e-meeting-to-composer.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-util/e-util.h"
+#include "composer/e-msg-composer.h"
+#include "composer/e-composer-from-header.h"
+#include "calendar/gui/e-comp-editor.h"
+#include "calendar/gui/e-comp-editor-page-attachments.h"
+#include "calendar/gui/itip-utils.h"
+
+#include "e-meeting-to-composer.h"
+
+/* Standard GObject macros */
+#define E_TYPE_MEETING_TO_COMPOSER \
+ (e_meeting_to_composer_get_type ())
+#define E_MEETING_TO_COMPOSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_MEETING_TO_COMPOSER, EMeetingToComposer))
+#define E_MEETING_TO_COMPOSER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_MEETING_TO_COMPOSER, EMeetingToComposerClass))
+#define E_IS_MEETING_TO_COMPOSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_MEETING_TO_COMPOSER))
+#define E_IS_MEETING_TO_COMPOSER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_MEETING_TO_COMPOSER))
+#define E_MEETING_TO_COMPOSER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_MEETING_TO_COMPOSER, EMeetingToComposerClass))
+
+typedef struct _EMeetingToComposer EMeetingToComposer;
+typedef struct _EMeetingToComposerClass EMeetingToComposerClass;
+
+struct _EMeetingToComposer {
+ EExtension parent;
+};
+
+struct _EMeetingToComposerClass {
+ EExtensionClass parent_class;
+};
+
+GType e_meeting_to_composer_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_DYNAMIC_TYPE (EMeetingToComposer, e_meeting_to_composer, E_TYPE_EXTENSION)
+
+static void
+meeting_to_composer_unref_nonull_object (gpointer ptr)
+{
+ if (ptr)
+ g_object_unref (ptr);
+}
+
+static gboolean
+meeting_to_composer_check_identity_source (ESource *source,
+ const gchar *address,
+ gchar **alias_name,
+ gchar **alias_address)
+{
+ ESourceMailIdentity *identity_extension;
+ GHashTable *aliases = NULL;
+ const gchar *text;
+ gboolean found = FALSE;
+
+ if (!E_IS_SOURCE (source) || !address ||
+ !e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY))
+ return FALSE;
+
+ identity_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
+
+ text = e_source_mail_identity_get_address (identity_extension);
+ found = text && g_ascii_strcasecmp (text, address) == 0;
+
+ if (!found) {
+ aliases = e_source_mail_identity_get_aliases_as_hash_table (identity_extension);
+ if (aliases) {
+ found = g_hash_table_contains (aliases, address);
+ if (found) {
+ if (alias_name)
+ *alias_name = g_strdup (g_hash_table_lookup (aliases, address));
+ if (alias_address)
+ *alias_address = g_strdup (address);
+ }
+ }
+ }
+
+ if (aliases)
+ g_hash_table_destroy (aliases);
+
+ return found;
+}
+
+static void
+meeting_to_composer_copy_attachments (ECompEditor *comp_editor,
+ EMsgComposer *composer)
+{
+ ECompEditorPage *page_attachments;
+ EAttachmentView *attachment_view;
+ EAttachmentStore *store;
+ GList *attachments, *link;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+ page_attachments = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS);
+ if (!page_attachments)
+ return;
+
+ store = e_comp_editor_page_attachments_get_store (E_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
+ attachments = e_attachment_store_get_attachments (store);
+
+ if (!attachments)
+ return;
+
+ attachment_view = e_msg_composer_get_attachment_view (composer);
+ store = e_attachment_view_get_store (attachment_view);
+
+ for (link = attachments; link; link = g_list_next (link)) {
+ EAttachment *attachment = link->data;
+
+ e_attachment_store_add_attachment (store, attachment);
+ }
+
+ g_list_free_full (attachments, g_object_unref);
+}
+
+static void
+meeting_to_composer_composer_created_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECompEditor *comp_editor = user_data;
+ EMsgComposer *composer;
+ EComposerHeaderTable *header_table;
+ gboolean did_updating;
+ icalcomponent *icalcomp;
+ icalproperty *prop;
+ const gchar *text;
+ GPtrArray *to_recips, *cc_recips;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+ composer = e_msg_composer_new_finish (result, &error);
+ if (!composer) {
+ g_warning ("%s: Faild to create message composer: %s", G_STRFUNC, error ? error->message :
"Unknown error");
+ return;
+ }
+
+ header_table = e_msg_composer_get_header_table (composer);
+
+ did_updating = e_comp_editor_get_updating (comp_editor);
+ /* Just a trick to not show validation errors when getting the component */
+ e_comp_editor_set_updating (comp_editor, TRUE);
+
+ icalcomp = icalcomponent_new_clone (e_comp_editor_get_component (comp_editor));
+ e_comp_editor_fill_component (comp_editor, icalcomp);
+
+ e_comp_editor_set_updating (comp_editor, did_updating);
+
+ /* Subject */
+ text = icalcomponent_get_summary (icalcomp);
+ if (text && *text)
+ e_composer_header_table_set_subject (header_table, text);
+
+ /* From */
+ prop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY);
+ if (prop) {
+ EComposerHeader *from_header;
+ const gchar *organizer;
+
+ from_header = e_composer_header_table_get_header (header_table, E_COMPOSER_HEADER_FROM);
+ organizer = itip_strip_mailto (icalproperty_get_organizer (prop));
+
+ if (organizer && *organizer && from_header) {
+ GtkComboBox *identities_combo;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint id_column;
+
+ identities_combo = GTK_COMBO_BOX (from_header->input_widget);
+ id_column = gtk_combo_box_get_id_column (identities_combo);
+ model = gtk_combo_box_get_model (identities_combo);
+
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ do {
+ ESource *source;
+ gchar *uid;
+ gboolean use_source;
+ gchar *alias_name = NULL;
+ gchar *alias_address = NULL;
+
+ gtk_tree_model_get (model, &iter, id_column, &uid, -1);
+ source = e_composer_header_table_ref_source (header_table, uid);
+
+ use_source = meeting_to_composer_check_identity_source (source,
organizer, &alias_name, &alias_address);
+ if (use_source)
+ e_composer_header_table_set_identity_uid (header_table, uid,
alias_name, alias_address);
+
+ g_clear_object (&source);
+ g_free (alias_name);
+ g_free (alias_address);
+ g_free (uid);
+
+ if (use_source)
+ break;
+ } while (gtk_tree_model_iter_next (model, &iter));
+ }
+ }
+ }
+
+ /* Recipients */
+ to_recips = g_ptr_array_new_with_free_func (meeting_to_composer_unref_nonull_object);
+ cc_recips = g_ptr_array_new_with_free_func (meeting_to_composer_unref_nonull_object);
+
+ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ prop;
+ prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
+ icalparameter *param;
+ icalparameter_role role = ICAL_ROLE_REQPARTICIPANT;
+ const gchar *name = NULL, *address;
+ EDestination *dest;
+
+ address = itip_strip_mailto (icalproperty_get_attendee (prop));
+ if (!address || !*address)
+ continue;
+
+ param = icalproperty_get_first_parameter (prop, ICAL_ROLE_PARAMETER);
+ if (param)
+ role = icalparameter_get_role (param);
+
+ if (role == ICAL_ROLE_NONPARTICIPANT || role == ICAL_ROLE_NONE)
+ continue;
+
+ param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+ if (param)
+ name = icalparameter_get_cn (param);
+
+ if (name && !*name)
+ name = NULL;
+
+ dest = e_destination_new ();
+ e_destination_set_name (dest, name);
+ e_destination_set_email (dest, address);
+
+ if (role == ICAL_ROLE_REQPARTICIPANT)
+ g_ptr_array_add (to_recips, dest);
+ else
+ g_ptr_array_add (cc_recips, dest);
+ }
+
+ if (to_recips->len > 0) {
+ g_ptr_array_add (to_recips, NULL);
+
+ e_composer_header_table_set_destinations_to (header_table, (EDestination **)
to_recips->pdata);
+ }
+
+ if (cc_recips->len > 0) {
+ g_ptr_array_add (cc_recips, NULL);
+
+ e_composer_header_table_set_destinations_cc (header_table, (EDestination **)
cc_recips->pdata);
+ }
+
+ g_ptr_array_free (to_recips, TRUE);
+ g_ptr_array_free (cc_recips, TRUE);
+
+ /* Body */
+ prop = icalcomponent_get_first_property (icalcomp, ICAL_DESCRIPTION_PROPERTY);
+ if (prop) {
+ text = icalproperty_get_description (prop);
+
+ if (text && *text) {
+ EHTMLEditor *html_editor;
+ EContentEditor *cnt_editor;
+
+ html_editor = e_msg_composer_get_editor (composer);
+ cnt_editor = e_html_editor_get_content_editor (html_editor);
+
+ e_content_editor_set_html_mode (cnt_editor, FALSE);
+ e_content_editor_insert_content (cnt_editor, text,
E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
+ }
+ }
+
+ /* Attachments */
+ meeting_to_composer_copy_attachments (comp_editor, composer);
+
+ gtk_window_present (GTK_WINDOW (composer));
+
+ gtk_widget_destroy (GTK_WIDGET (comp_editor));
+ icalcomponent_free (icalcomp);
+}
+
+static void
+action_meeting_to_composer_cb (GtkAction *action,
+ ECompEditor *comp_editor)
+{
+ icalcomponent *icalcomp;
+ icalcomponent_kind kind;
+ const gchar *prompt_key;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+ icalcomp = e_comp_editor_get_component (comp_editor);
+ kind = icalcomp ? icalcomponent_isa (icalcomp) : ICAL_VEVENT_COMPONENT;
+
+ if (kind == ICAL_VTODO_COMPONENT)
+ prompt_key = "mail-composer:prompt-task-to-composer";
+ else if (kind == ICAL_VJOURNAL_COMPONENT)
+ prompt_key = "mail-composer:prompt-memo-to-composer";
+ else
+ prompt_key = "mail-composer:prompt-event-to-composer";
+
+ if (!e_util_prompt_user (GTK_WINDOW (comp_editor), NULL, NULL, prompt_key, NULL))
+ return;
+
+ e_msg_composer_new (e_comp_editor_get_shell (comp_editor),
+ meeting_to_composer_composer_created_cb, comp_editor);
+}
+
+static void
+e_meeting_to_composer_setup_ui (ECompEditor *comp_editor)
+{
+ const gchar *ui =
+ "<ui>"
+ " <menubar action='main-menu'>"
+ " <menu action='file-menu'>"
+ " <placeholder name='custom-actions-placeholder'>"
+ " <menuitem action='meeting-to-composer-action'/>"
+ " </placeholder>"
+ " </menu>"
+ " </menubar>"
+ "</ui>";
+
+ GtkActionEntry entries[] = {
+ { "meeting-to-composer-action",
+ "mail-message-new",
+ N_("Convert to M_essage"),
+ NULL,
+ N_("Convert to the mail message"),
+ G_CALLBACK (action_meeting_to_composer_cb) }
+ };
+
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+ ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+ action_group = e_comp_editor_get_action_group (comp_editor, "individual");
+
+ gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), comp_editor);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+ if (error) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+meeting_to_composer_constructed (GObject *object)
+{
+ ECompEditor *comp_editor;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_meeting_to_composer_parent_class)->constructed (object);
+
+ comp_editor = E_COMP_EDITOR (e_extension_get_extensible (E_EXTENSION (object)));
+
+ e_meeting_to_composer_setup_ui (comp_editor);
+}
+
+static void
+e_meeting_to_composer_class_init (EMeetingToComposerClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = meeting_to_composer_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_COMP_EDITOR;
+}
+
+static void
+e_meeting_to_composer_class_finalize (EMeetingToComposerClass *class)
+{
+}
+
+static void
+e_meeting_to_composer_init (EMeetingToComposer *extension)
+{
+}
+
+void
+e_meeting_to_composer_type_register (GTypeModule *type_module)
+{
+ /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+ * function, so we have to wrap it with a public function in
+ * order to register types from a separate compilation unit. */
+ e_meeting_to_composer_register_type (type_module);
+}
diff --git a/src/modules/composer-to-meeting/e-meeting-to-composer.h
b/src/modules/composer-to-meeting/e-meeting-to-composer.h
new file mode 100644
index 0000000..ecc7458
--- /dev/null
+++ b/src/modules/composer-to-meeting/e-meeting-to-composer.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_MEETING_TO_COMPOSER_H
+#define E_MEETING_TO_COMPOSER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void e_meeting_to_composer_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_MEETING_TO_COMPOSER_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]