[evolution] Bug 773548 - Be able to convert composed mail to meeting request



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]