[evolution/wip/webkit2] Make calendar editors modular and non-UI-blocking



commit b9f9e3a5d9e933e1f84b64244cc3c4559fc0e6b6
Author: Milan Crha <mcrha redhat com>
Date:   Mon Nov 2 17:31:17 2015 +0100

    Make calendar editors modular and non-UI-blocking

 calendar/calendar.error.xml                       |   57 +
 calendar/gui/Makefile.am                          |   31 +-
 calendar/gui/calendar-config.c                    |   45 +
 calendar/gui/calendar-config.h                    |    4 +
 calendar/gui/comp-util.c                          |  131 +-
 calendar/gui/comp-util.h                          |   17 +
 calendar/gui/dialogs/Makefile.am                  |  125 -
 calendar/gui/dialogs/alarm-dialog.c               | 1344 ------
 calendar/gui/dialogs/alarm-dialog.h               |   45 -
 calendar/gui/dialogs/alarm-dialog.ui              | 1070 -----
 calendar/gui/dialogs/alarm-list-dialog.c          |  349 --
 calendar/gui/dialogs/alarm-list-dialog.h          |   52 -
 calendar/gui/dialogs/alarm-list-dialog.ui         |  164 -
 calendar/gui/dialogs/cancel-comp.c                |  115 -
 calendar/gui/dialogs/cancel-comp.h                |   33 -
 calendar/gui/dialogs/changed-comp.c               |  121 -
 calendar/gui/dialogs/changed-comp.h               |   30 -
 calendar/gui/dialogs/comp-editor-page.c           |  445 --
 calendar/gui/dialogs/comp-editor-page.h           |  132 -
 calendar/gui/dialogs/comp-editor-util.c           |  422 --
 calendar/gui/dialogs/comp-editor-util.h           |   51 -
 calendar/gui/dialogs/comp-editor.c                | 4578 ---------------------
 calendar/gui/dialogs/comp-editor.h                |  285 --
 calendar/gui/dialogs/copy-source-dialog.c         |  257 --
 calendar/gui/dialogs/copy-source-dialog.h         |   36 -
 calendar/gui/dialogs/delete-comp.c                |  261 --
 calendar/gui/dialogs/delete-comp.h                |   37 -
 calendar/gui/dialogs/e-delegate-dialog.c          |  297 --
 calendar/gui/dialogs/e-delegate-dialog.h          |   81 -
 calendar/gui/dialogs/e-delegate-dialog.ui         |  105 -
 calendar/gui/dialogs/event-editor.c               |  802 ----
 calendar/gui/dialogs/event-editor.h               |   74 -
 calendar/gui/dialogs/event-page.c                 | 3838 -----------------
 calendar/gui/dialogs/event-page.h                 |  114 -
 calendar/gui/dialogs/event-page.ui                | 1076 -----
 calendar/gui/dialogs/goto-dialog.c                |  301 --
 calendar/gui/dialogs/goto-dialog.h                |   38 -
 calendar/gui/dialogs/goto-dialog.ui               |  181 -
 calendar/gui/dialogs/memo-editor.c                |  178 -
 calendar/gui/dialogs/memo-editor.h                |   75 -
 calendar/gui/dialogs/memo-page.c                  | 1335 ------
 calendar/gui/dialogs/memo-page.h                  |   77 -
 calendar/gui/dialogs/memo-page.ui                 |  459 ---
 calendar/gui/dialogs/recur-comp.c                 |  157 -
 calendar/gui/dialogs/recur-comp.h                 |   42 -
 calendar/gui/dialogs/recurrence-page.c            | 2684 ------------
 calendar/gui/dialogs/recurrence-page.h            |   78 -
 calendar/gui/dialogs/recurrence-page.ui           |  566 ---
 calendar/gui/dialogs/save-comp.c                  |   62 -
 calendar/gui/dialogs/save-comp.h                  |   33 -
 calendar/gui/dialogs/schedule-page.c              |  556 ---
 calendar/gui/dialogs/schedule-page.h              |   79 -
 calendar/gui/dialogs/schedule-page.ui             |   20 -
 calendar/gui/dialogs/select-source-dialog.c       |   84 -
 calendar/gui/dialogs/select-source-dialog.h       |   36 -
 calendar/gui/dialogs/send-comp.c                  |  310 --
 calendar/gui/dialogs/send-comp.h                  |   33 -
 calendar/gui/dialogs/task-editor.c                |  462 ---
 calendar/gui/dialogs/task-editor.h                |   74 -
 calendar/gui/dialogs/task-page.c                  | 2777 -------------
 calendar/gui/dialogs/task-page.h                  |  101 -
 calendar/gui/dialogs/task-page.ui                 |  782 ----
 calendar/gui/e-alarm-list.c                       |   39 +-
 calendar/gui/e-cal-dialogs.c                      | 1304 ++++++
 calendar/gui/e-cal-dialogs.h                      |   76 +
 calendar/gui/e-cal-list-view.c                    |    5 -
 calendar/gui/e-cal-model-calendar.c               |    5 +-
 calendar/gui/e-cal-model.c                        |    5 +-
 calendar/gui/e-cal-ops.c                          |  153 +-
 calendar/gui/e-calendar-view.c                    |   74 +-
 calendar/gui/e-calendar-view.h                    |    6 +-
 calendar/gui/e-comp-editor-event.c                |  724 ++++
 calendar/gui/e-comp-editor-event.h                |   63 +
 calendar/gui/e-comp-editor-memo.c                 |  178 +
 calendar/gui/e-comp-editor-memo.h                 |   63 +
 calendar/gui/e-comp-editor-page-attachments.c     |  924 +++++
 calendar/gui/e-comp-editor-page-attachments.h     |   73 +
 calendar/gui/e-comp-editor-page-general.c         | 1844 +++++++++
 calendar/gui/e-comp-editor-page-general.h         |  119 +
 calendar/gui/e-comp-editor-page-recurrence.c      | 2369 +++++++++++
 calendar/gui/e-comp-editor-page-recurrence.h      |   64 +
 calendar/gui/e-comp-editor-page-reminders.c       | 2339 +++++++++++
 calendar/gui/e-comp-editor-page-reminders.h       |   64 +
 calendar/gui/e-comp-editor-page-schedule.c        |  605 +++
 calendar/gui/e-comp-editor-page-schedule.h        |   71 +
 calendar/gui/e-comp-editor-page.c                 |  401 ++
 calendar/gui/e-comp-editor-page.h                 |   99 +
 calendar/gui/e-comp-editor-property-part.c        | 1559 +++++++
 calendar/gui/e-comp-editor-property-part.h        |  393 ++
 calendar/gui/e-comp-editor-property-parts.c       | 1553 +++++++
 calendar/gui/e-comp-editor-property-parts.h       |   66 +
 calendar/gui/e-comp-editor-task.c                 |  568 +++
 calendar/gui/e-comp-editor-task.h                 |   63 +
 calendar/gui/e-comp-editor.c                      | 3267 +++++++++++++++
 calendar/gui/e-comp-editor.h                      |  185 +
 calendar/gui/e-date-time-list.c                   |   78 +-
 calendar/gui/e-date-time-list.h                   |    9 +-
 calendar/gui/e-day-view.c                         |   25 +-
 calendar/gui/e-memo-table.c                       |    5 +-
 calendar/gui/{dialogs => }/e-send-options-utils.c |    1 -
 calendar/gui/{dialogs => }/e-send-options-utils.h |    0
 calendar/gui/e-task-table.c                       |   10 +-
 calendar/gui/e-week-view.c                        |   11 +-
 calendar/gui/ea-cal-view.c                        |    1 -
 calendar/gui/itip-utils.c                         |  170 +-
 calendar/gui/itip-utils.h                         |   20 +-
 configure.ac                                      |    1 -
 e-util/e-alert-bar.c                              |    6 +-
 e-util/e-widget-undo.c                            |    1 +
 modules/calendar/e-cal-base-shell-backend.c       |   90 +-
 modules/calendar/e-cal-base-shell-view.c          |    4 +-
 modules/calendar/e-cal-shell-content.c            |    1 +
 modules/calendar/e-cal-shell-view-actions.c       |   15 +-
 modules/calendar/e-cal-shell-view-memopad.c       |    5 +-
 modules/calendar/e-cal-shell-view-private.h       |    5 -
 modules/calendar/e-cal-shell-view-taskpad.c       |    5 +-
 modules/calendar/e-memo-shell-view-actions.c      |    5 +-
 modules/calendar/e-memo-shell-view-private.h      |    1 -
 modules/calendar/e-task-shell-backend.c           |    1 -
 modules/calendar/e-task-shell-view-actions.c      |    5 +-
 modules/calendar/e-task-shell-view-private.h      |    1 -
 modules/settings/Makefile.am                      |    2 -
 modules/settings/e-settings-comp-editor.c         |  228 -
 modules/settings/e-settings-comp-editor.h         |   63 -
 modules/settings/evolution-module-settings.c      |    2 -
 plugins/mail-to-task/mail-to-task.c               |   92 +-
 po/POTFILES.in                                    |   42 +-
 127 files changed, 19714 insertions(+), 28716 deletions(-)
---
diff --git a/calendar/calendar.error.xml b/calendar/calendar.error.xml
index a834acd..f263335 100644
--- a/calendar/calendar.error.xml
+++ b/calendar/calendar.error.xml
@@ -188,6 +188,20 @@
     <button _label="_Send" response="GTK_RESPONSE_YES"/>
   </error>
 
+  <error id="prompt-send-memo" type="question" default="GTK_RESPONSE_YES">
+    <_primary>Would you like to send this memo to participants?</_primary>
+    <_secondary>Email invitations will be sent to all participants and allow them to accept this 
memo.</_secondary>
+    <button _label="Do _not Send" response="GTK_RESPONSE_NO"/>
+    <button _label="_Send" response="GTK_RESPONSE_YES"/>
+  </error>
+
+  <error id="prompt-send-updated-memo-info" type="question" default="GTK_RESPONSE_YES">
+    <_primary>Would you like to send updated memo information to participants?</_primary>
+    <_secondary>Sending updated information allows other participants to keep their memo lists up to 
date.</_secondary>
+    <button _label="Do _not Send" response="GTK_RESPONSE_NO"/>
+    <button _label="_Send" response="GTK_RESPONSE_YES"/>
+  </error>
+
   <error id="editor-error" type="error">
     <_primary>Editor could not be loaded.</_primary>
     <secondary>{0}.</secondary>
@@ -285,6 +299,12 @@
     <_secondary>The memo list is not marked for offline usage.</_secondary>
   </error>
 
+  <error id="failed-add-timezone" type="error" default="GTK_RESPONSE_YES">
+    <!-- Translators: {0} is the name of the calendar/memo list/task list. -->
+    <_primary>Failed to add timezone to '{0}'</_primary>
+    <secondary>{1}</secondary>
+  </error>
+
   <error id="failed-open-calendar" type="error" default="GTK_RESPONSE_YES">
     <!-- Translators: {0} is the name of the calendar. -->
     <_primary>Failed to open calendar '{0}'</_primary>
@@ -451,6 +471,24 @@
     <secondary>{1}</secondary>
   </error>
 
+  <error id="failed-get-event" type="error" default="GTK_RESPONSE_YES">
+    <!-- Translators: {0} is the name of the calendar. -->
+    <_primary>Failed to get an event from the calendar '{0}'</_primary>
+    <secondary>{1}</secondary>
+  </error>
+
+  <error id="failed-get-task" type="error" default="GTK_RESPONSE_YES">
+    <!-- Translators: {0} is the name of the task list. -->
+    <_primary>Failed to get a task from the task list '{0}'</_primary>
+    <secondary>{1}</secondary>
+  </error>
+
+  <error id="failed-get-memo" type="error" default="GTK_RESPONSE_YES">
+    <!-- Translators: {0} is the name of the memo list. -->
+    <_primary>Failed to get a memo from the memo list '{0}'</_primary>
+    <secondary>{1}</secondary>
+  </error>
+
   <error id="status-copy-event" type="warning">
     <!-- Translators: {0} is the name of the calendar. -->
     <_primary>Copying an event into the calendar '{0}'</_primary>
@@ -504,4 +542,23 @@
     <secondary>{1}</secondary>
   </error>
 
+  <error id="comp-editor-failed-validate" type="error">
+    <primary>{0}</primary>
+  </error>
+
+  <error id="comp-editor-information" type="information">
+    <primary>{0}</primary>
+    <secondary>{1}</secondary>
+  </error>
+
+  <error id="comp-editor-warning" type="warning">
+    <primary>{0}</primary>
+    <secondary>{1}</secondary>
+  </error>
+
+  <error id="comp-editor-error" type="error">
+    <primary>{0}</primary>
+    <secondary>{1}</secondary>
+  </error>
+
 </error-list>
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index bbf8e10..07cd5f2 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -1,5 +1,3 @@
-SUBDIRS = dialogs
-
 privsolib_LTLIBRARIES = libevolution-calendar.la
 
 ecalendarincludedir = $(privincludedir)/calendar/gui
@@ -13,6 +11,7 @@ ecalendarinclude_HEADERS =                    \
        e-cal-config.h                          \
        e-cal-data-model.h                      \
        e-cal-data-model-subscriber.h           \
+       e-cal-dialogs.h                         \
        e-cal-event.h                           \
        e-cal-list-view.h                       \
        e-cal-model-calendar.h                  \
@@ -20,6 +19,18 @@ ecalendarinclude_HEADERS =                   \
        e-cal-ops.h                             \
        e-calendar-view.h                       \
        e-cell-date-edit-text.h                 \
+       e-comp-editor.h                         \
+       e-comp-editor-event.h                   \
+       e-comp-editor-memo.h                    \
+       e-comp-editor-page-attachments.h        \
+       e-comp-editor-page-general.h            \
+       e-comp-editor-page-recurrence.h         \
+       e-comp-editor-page-reminders.h          \
+       e-comp-editor-page-schedule.h           \
+       e-comp-editor-page.h                    \
+       e-comp-editor-property-part.h           \
+       e-comp-editor-property-parts.h          \
+       e-comp-editor-task.h                    \
        e-date-time-list.h                      \
        e-day-view-layout.h                     \
        e-day-view-main-item.h                  \
@@ -35,6 +46,7 @@ ecalendarinclude_HEADERS =                    \
        e-meeting-utils.h                       \
        e-select-names-editable.h               \
        e-select-names-renderer.h               \
+       e-send-options-utils.h                  \
        e-week-view-event-item.h                \
        e-week-view-layout.h                    \
        e-week-view-main-item.h                 \
@@ -96,6 +108,7 @@ libevolution_calendar_la_SOURCES = \
        e-cal-data-model.h                      \
        e-cal-data-model-subscriber.c           \
        e-cal-data-model-subscriber.h           \
+       e-cal-dialogs.c                         \
        e-cal-event.c                           \
        e-cal-event.h                           \
        e-cal-list-view.c                       \
@@ -114,6 +127,18 @@ libevolution_calendar_la_SOURCES = \
        e-calendar-view.h                       \
        e-cell-date-edit-text.h                 \
        e-cell-date-edit-text.c                 \
+       e-comp-editor.c                         \
+       e-comp-editor-event.c                   \
+       e-comp-editor-memo.c                    \
+       e-comp-editor-page-attachments.c        \
+       e-comp-editor-page-general.c            \
+       e-comp-editor-page-recurrence.c         \
+       e-comp-editor-page-reminders.c          \
+       e-comp-editor-page-schedule.c           \
+       e-comp-editor-page.c                    \
+       e-comp-editor-property-part.c           \
+       e-comp-editor-property-parts.c          \
+       e-comp-editor-task.c                    \
        e-date-time-list.c                      \
        e-date-time-list.h                      \
        e-day-view-layout.c                     \
@@ -147,6 +172,7 @@ libevolution_calendar_la_SOURCES = \
        e-select-names-editable.h               \
        e-select-names-renderer.c               \
        e-select-names-renderer.h               \
+       e-send-options-utils.c                  \
        e-task-table.c                          \
        e-task-table.h                          \
        e-week-view-event-item.c                \
@@ -199,7 +225,6 @@ libevolution_calendar_la_LIBADD =                                   \
        $(top_builddir)/addressbook/gui/contact-editor/libecontacteditor.la \
        $(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \
        $(top_builddir)/shell/libevolution-shell.la                     \
-       $(top_builddir)/calendar/gui/dialogs/libcal-dialogs.la          \
        $(top_builddir)/calendar/importers/libevolution-calendar-importers.la \
        $(top_builddir)/e-util/libevolution-util.la                     \
        $(EVOLUTION_DATA_SERVER_LIBS)                                   \
diff --git a/calendar/gui/calendar-config.c b/calendar/gui/calendar-config.c
index 06d9f4a..0a0d238 100644
--- a/calendar/gui/calendar-config.c
+++ b/calendar/gui/calendar-config.c
@@ -455,3 +455,48 @@ calendar_config_get_prefer_meeting (void)
 
        return prefer_meeting;
 }
+
+GDateWeekday
+calendar_config_get_week_start_day (void)
+{
+       GSettings *settings;
+       GDateWeekday res;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+
+       res = g_settings_get_enum (settings, "week-start-day-name");
+
+       g_object_unref (settings);
+
+       return res;
+}
+
+gint
+calendar_config_get_default_reminder_interval (void)
+{
+       GSettings *settings;
+       gint res;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+
+       res = g_settings_get_int (settings, "default-reminder-interval");
+
+       g_object_unref (settings);
+
+       return res;
+}
+
+EDurationType
+calendar_config_get_default_reminder_units (void)
+{
+       GSettings *settings;
+       EDurationType res;
+
+       settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+
+       res = g_settings_get_enum (settings, "default-reminder-units");
+
+       g_object_unref (settings);
+
+       return res;
+}
diff --git a/calendar/gui/calendar-config.h b/calendar/gui/calendar-config.h
index d2a0bf0..ff1c158 100644
--- a/calendar/gui/calendar-config.h
+++ b/calendar/gui/calendar-config.h
@@ -75,4 +75,8 @@ void     calendar_config_add_notification_month_scroll_by_week (CalendarConfigCh
 
 gboolean calendar_config_get_prefer_meeting (void);
 
+GDateWeekday   calendar_config_get_week_start_day              (void);
+gint           calendar_config_get_default_reminder_interval   (void);
+EDurationType  calendar_config_get_default_reminder_units      (void);
+
 #endif /* _CALENDAR_CONFIG_H_ */
diff --git a/calendar/gui/comp-util.c b/calendar/gui/comp-util.c
index 114a4bc..f37fa33 100644
--- a/calendar/gui/comp-util.c
+++ b/calendar/gui/comp-util.c
@@ -31,7 +31,6 @@
 #include "calendar-config.h"
 #include "comp-util.h"
 #include "e-calendar-view.h"
-#include "dialogs/delete-comp.h"
 
 #include "shell/e-shell-window.h"
 #include "shell/e-shell-view.h"
@@ -1307,3 +1306,133 @@ cal_comp_transfer_item_to_sync (ECalClient *src_client,
 
        return success;
 }
+
+void
+cal_comp_util_update_tzid_parameter (icalproperty *prop,
+                                    const struct icaltimetype tt)
+{
+       icalparameter *param;
+       const gchar *tzid = NULL;
+
+       g_return_if_fail (prop != NULL);
+
+       if (!icaltime_is_valid_time (tt) ||
+           icaltime_is_null_time (tt))
+               return;
+
+       param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+       if (tt.zone)
+               tzid = icaltimezone_get_tzid ((icaltimezone *) tt.zone);
+
+       if (tt.zone && tzid && *tzid && !tt.is_utc && !tt.is_date) {
+               if (param) {
+                       icalparameter_set_tzid (param, (gchar *) tzid);
+               } else {
+                       param = icalparameter_new_tzid ((gchar *) tzid);
+                       icalproperty_add_parameter (prop, param);
+               }
+       } else if (param) {
+               icalproperty_remove_parameter (prop, ICAL_TZID_PARAMETER);
+       }
+}
+
+/* Returns <0 for time before today, 0 for today, >0 for after today (future) */
+gint
+cal_comp_util_compare_time_with_today (const struct icaltimetype time_tt)
+{
+       struct icaltimetype now_tt;
+
+       if (icaltime_is_null_time (time_tt))
+               return 0;
+
+       if (time_tt.is_date) {
+               now_tt = icaltime_today ();
+               return icaltime_compare_date_only (time_tt, now_tt);
+       } else {
+               now_tt = icaltime_current_time_with_zone (time_tt.zone);
+               now_tt.zone = time_tt.zone;
+       }
+
+       return icaltime_compare (time_tt, now_tt);
+}
+
+/* Returns whether removed any */
+gboolean
+cal_comp_util_remove_all_properties (icalcomponent *component,
+                                    icalproperty_kind kind)
+{
+       icalproperty *prop;
+       gboolean removed_any = FALSE;
+
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       while (prop = icalcomponent_get_first_property (component, kind), prop) {
+               icalcomponent_remove_property (component, prop);
+               icalproperty_free (prop);
+
+               removed_any = TRUE;
+       }
+
+       return removed_any;
+}
+
+gboolean
+cal_comp_util_have_in_new_attendees (const GSList *new_attendees_mails,
+                                    const gchar *eml)
+{
+       const GSList *link;
+
+       if (!eml)
+               return FALSE;
+
+       for (link = new_attendees_mails; link; link = g_slist_next (link)) {
+               if (link->data && g_ascii_strcasecmp (eml, link->data) == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+free_slist_strs (gpointer data)
+{
+       GSList *lst = data;
+
+       if (lst) {
+               g_slist_foreach (lst, (GFunc) g_free, NULL);
+               g_slist_free (lst);
+       }
+}
+
+/**
+ * cal_comp_util_copy_new_attendees:
+ * @des: Component, to copy to.
+ * @src: Component, to copy from.
+ *
+ * Copies "new-attendees" information from @src to @des component.
+ **/
+void
+cal_comp_util_copy_new_attendees (ECalComponent *des,
+                                 ECalComponent *src)
+{
+       GSList *copy = NULL, *l;
+
+       g_return_if_fail (src != NULL);
+       g_return_if_fail (des != NULL);
+
+       for (l = g_object_get_data (G_OBJECT (src), "new-attendees"); l; l = l->next) {
+               copy = g_slist_append (copy, g_strdup (l->data));
+       }
+
+       g_object_set_data_full (G_OBJECT (des), "new-attendees", copy, free_slist_strs);
+}
+
+/* Takes ownership of the 'emails' */
+void
+cal_comp_util_set_added_attendees_mails (ECalComponent *comp,
+                                        GSList *emails)
+{
+       g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+       g_object_set_data_full (G_OBJECT (comp), "new-attendees", emails, free_slist_strs);
+}
diff --git a/calendar/gui/comp-util.h b/calendar/gui/comp-util.h
index a3b2350..6871c72 100644
--- a/calendar/gui/comp-util.h
+++ b/calendar/gui/comp-util.h
@@ -122,5 +122,22 @@ gboolean cal_comp_transfer_item_to_sync            (ECalClient *src_client,
                                                 gboolean do_copy,
                                                 GCancellable *cancellable,
                                                 GError **error);
+void           cal_comp_util_update_tzid_parameter
+                                               (icalproperty *prop,
+                                                const struct icaltimetype tt);
+gint           cal_comp_util_compare_time_with_today
+                                               (const struct icaltimetype time_tt);
+gboolean       cal_comp_util_remove_all_properties
+                                               (icalcomponent *component,
+                                                icalproperty_kind kind);
+gboolean       cal_comp_util_have_in_new_attendees
+                                               (const GSList *new_attendees_mails,
+                                                const gchar *eml);
+void           cal_comp_util_copy_new_attendees
+                                               (ECalComponent *des,
+                                                ECalComponent *src);
+void           cal_comp_util_set_added_attendees_mails
+                                               (ECalComponent *comp,
+                                                GSList *emails);
 
 #endif
diff --git a/calendar/gui/e-alarm-list.c b/calendar/gui/e-alarm-list.c
index 19b47e0..353b61f 100644
--- a/calendar/gui/e-alarm-list.c
+++ b/calendar/gui/e-alarm-list.c
@@ -366,31 +366,31 @@ get_alarm_duration_string (struct icaldurationtype *duration)
        have_something = FALSE;
 
        if (duration->days >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d days before start of appointment" */
+               /* Translator: Entire string is like "Pop up an alert %d days before start" */
                g_string_printf (string, ngettext ("%d day", "%d days", duration->days), duration->days);
                have_something = TRUE;
        }
 
        if (duration->weeks >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d weeks before start of appointment" */
+               /* Translator: Entire string is like "Pop up an alert %d weeks before start" */
                g_string_printf (string, ngettext ("%d week","%d weeks", duration->weeks), duration->weeks);
                have_something = TRUE;
        }
 
        if (duration->hours >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d hours before start of appointment" */
+               /* Translator: Entire string is like "Pop up an alert %d hours before start" */
                g_string_printf (string, ngettext ("%d hour", "%d hours", duration->hours), duration->hours);
                have_something = TRUE;
        }
 
        if (duration->minutes >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d minutes before start of appointment" 
*/
+               /* Translator: Entire string is like "Pop up an alert %d minutes before start" */
                g_string_printf (string, ngettext ("%d minute", "%d minutes", duration->minutes), 
duration->minutes);
                have_something = TRUE;
        }
 
        if (duration->seconds >= 1) {
-               /* Translator: Entire string is like "Pop up an alert %d seconds before start of appointment" 
*/
+               /* Translator: Entire string is like "Pop up an alert %d seconds before start" */
                g_string_printf (string, ngettext ("%d second", "%d seconds", duration->seconds), 
duration->seconds);
                have_something = TRUE;
        }
@@ -410,32 +410,33 @@ get_alarm_string (ECalComponentAlarm *alarm)
 {
        ECalComponentAlarmAction action;
        ECalComponentAlarmTrigger trigger;
-       gchar *base, *str = NULL, *dur;
+       const gchar *base;
+       gchar *str = NULL, *dur;
 
        e_cal_component_alarm_get_action (alarm, &action);
        e_cal_component_alarm_get_trigger (alarm, &trigger);
 
        switch (action) {
        case E_CAL_COMPONENT_ALARM_AUDIO:
-               base = _("Play a sound");
+               base = C_("cal-reminders", "Play a sound");
                break;
 
        case E_CAL_COMPONENT_ALARM_DISPLAY:
-               base = _("Pop up an alert");
+               base = C_("cal-reminders", "Pop up an alert");
                break;
 
        case E_CAL_COMPONENT_ALARM_EMAIL:
-               base = _("Send an email");
+               base = C_("cal-reminders", "Send an email");
                break;
 
        case E_CAL_COMPONENT_ALARM_PROCEDURE:
-               base = _("Run a program");
+               base = C_("cal-reminders", "Run a program");
                break;
 
        case E_CAL_COMPONENT_ALARM_NONE:
        case E_CAL_COMPONENT_ALARM_UNKNOWN:
        default:
-               base = _("Unknown action to be performed");
+               base = C_("cal-reminders", "Unknown action to be performed");
                break;
        }
 
@@ -450,20 +451,20 @@ get_alarm_string (ECalComponentAlarm *alarm)
                                /*Translator: The first %s refers to the base, which would be actions like
                                 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
                                str = g_strdup_printf (
-                                       _("%s %s before the start of the appointment"),
+                                       C_("cal-reminders", "%s %s before the start"),
                                        base, dur);
                        else
                                /*Translator: The first %s refers to the base, which would be actions like
                                 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
                                str = g_strdup_printf (
-                                       _("%s %s after the start of the appointment"),
+                                       C_("cal-reminders", "%s %s after the start"),
                                        base, dur);
 
                        g_free (dur);
                } else
                        /*Translator: The %s refers to the base, which would be actions like
                         * "Play a sound" */
-                       str = g_strdup_printf (_("%s at the start of the appointment"), base);
+                       str = g_strdup_printf (C_("cal-reminders", "%s at the start"), base);
 
                break;
 
@@ -475,20 +476,20 @@ get_alarm_string (ECalComponentAlarm *alarm)
                                /* Translator: The first %s refers to the base, which would be actions like
                                 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
                                str = g_strdup_printf (
-                                       _("%s %s before the end of the appointment"),
+                                       C_("cal-reminders", "%s %s before the end"),
                                        base, dur);
                        else
                                /* Translator: The first %s refers to the base, which would be actions like
                                 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
                                str = g_strdup_printf (
-                                       _("%s %s after the end of the appointment"),
+                                       C_("cal-reminders", "%s %s after the end"),
                                        base, dur);
 
                        g_free (dur);
                } else
                        /* Translator: The %s refers to the base, which would be actions like
                         * "Play a sound" */
-                       str = g_strdup_printf (_("%s at the end of the appointment"), base);
+                       str = g_strdup_printf (C_("cal-reminders", "%s at the end"), base);
 
                break;
 
@@ -512,7 +513,7 @@ get_alarm_string (ECalComponentAlarm *alarm)
 
                /* Translator: The first %s refers to the base, which would be actions like
                 * "Play a Sound". Second %s is an absolute time, e.g. "10:00AM" */
-               str = g_strdup_printf (_("%s at %s"), base, buf);
+               str = g_strdup_printf (C_("cal-reminders", "%s at %s"), base, buf);
 
                break; }
 
@@ -520,7 +521,7 @@ get_alarm_string (ECalComponentAlarm *alarm)
        default:
                /* Translator: The %s refers to the base, which would be actions like
                 * "Play a sound". "Trigger types" are absolute or relative dates */
-               str = g_strdup_printf (_("%s for an unknown trigger type"), base);
+               str = g_strdup_printf (C_("cal-reminders", "%s for an unknown trigger type"), base);
                break;
        }
 
diff --git a/calendar/gui/e-cal-dialogs.c b/calendar/gui/e-cal-dialogs.c
new file mode 100644
index 0000000..1be2f02
--- /dev/null
+++ b/calendar/gui/e-cal-dialogs.c
@@ -0,0 +1,1304 @@
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     JP Rosevear <jpr ximian com>
+ *     Rodrigo Moya <rodrigo ximian com>
+ *     Federico Mena-Quintero <federico ximian com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <libecal/libecal.h>
+#include <e-util/e-util.h>
+
+#include "calendar-config.h"
+#include "itip-utils.h"
+#include "tag-calendar.h"
+
+#include "e-cal-dialogs.h"
+
+/* is_past_event:
+ *
+ * returns TRUE if @comp is in the past, FALSE otherwise.
+ * Comparision is based only on date part, time part is ignored.
+ */
+static gboolean
+is_past_event (ECalComponent *comp)
+{
+       ECalComponentDateTime end_date;
+       gboolean res;
+
+       if (!comp) return TRUE;
+
+       e_cal_component_get_dtend (comp, &end_date);
+       res = icaltime_compare_date_only (
+               *end_date.value,
+               icaltime_current_time_with_zone (
+               icaltime_get_timezone (*end_date.value))) == -1;
+       e_cal_component_free_datetime (&end_date);
+
+       return res;
+}
+
+/**
+ * e_cal_dialogs_cancel_component:
+ *
+ * Pops up a dialog box asking the user whether he wants to send a
+ * cancel and delete an iTip/iMip message
+ *
+ * Return value: TRUE if the user clicked Yes, FALSE otherwise.
+ **/
+gboolean
+e_cal_dialogs_cancel_component (GtkWindow *parent,
+                               ECalClient *cal_client,
+                               ECalComponent *comp,
+                               gboolean deleting)
+{
+       ECalComponentVType vtype;
+       const gchar *id;
+
+       if (deleting && e_cal_client_check_save_schedules (cal_client))
+               return TRUE;
+
+       vtype = e_cal_component_get_vtype (comp);
+
+       switch (vtype) {
+       case E_CAL_COMPONENT_EVENT:
+               if (is_past_event (comp)) {
+                       /* don't ask neither send notification to others on past events */
+                       return FALSE;
+               }
+               if (deleting)
+                       id = "calendar:prompt-cancel-meeting";
+               else
+                       id = "calendar:prompt-delete-meeting";
+               break;
+
+       case E_CAL_COMPONENT_TODO:
+               if (deleting)
+                       id = "calendar:prompt-cancel-task";
+               else
+                       id = "calendar:prompt-delete-task";
+               break;
+
+       case E_CAL_COMPONENT_JOURNAL:
+               if (deleting)
+                       id = "calendar:prompt-cancel-memo";
+               else
+                       id = "calendar:prompt-delete-memo";
+               break;
+
+       default:
+               g_message (G_STRLOC ": Cannot handle object of type %d", vtype);
+               return FALSE;
+       }
+
+       if (e_alert_run_dialog_for_args (parent, id, NULL) == GTK_RESPONSE_YES)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+typedef struct {
+       ECalModel *model;
+       ESource *from_source;
+       ESource *to_source;
+       ECalClient *to_client;
+       const gchar *extension_name;
+} CopySourceData;
+
+static void
+copy_source_data_free (gpointer ptr)
+{
+       CopySourceData *csd = ptr;
+
+       if (csd) {
+               if (csd->to_client)
+                       e_cal_model_emit_object_created (csd->model, csd->to_client);
+
+               g_clear_object (&csd->model);
+               g_clear_object (&csd->from_source);
+               g_clear_object (&csd->to_source);
+               g_clear_object (&csd->to_client);
+               g_free (csd);
+       }
+}
+
+struct ForeachTzidData
+{
+       ECalClient *from_client;
+       ECalClient *to_client;
+       gboolean success;
+       GCancellable *cancellable;
+       GError **error;
+};
+
+static void
+add_timezone_to_cal_cb (icalparameter *param,
+                        gpointer data)
+{
+       struct ForeachTzidData *ftd = data;
+       icaltimezone *tz = NULL;
+       const gchar *tzid;
+
+       g_return_if_fail (ftd != NULL);
+       g_return_if_fail (ftd->from_client != NULL);
+       g_return_if_fail (ftd->to_client != NULL);
+
+       if (!ftd->success)
+               return;
+
+       tzid = icalparameter_get_tzid (param);
+       if (!tzid || !*tzid)
+               return;
+
+       if (g_cancellable_set_error_if_cancelled (ftd->cancellable, ftd->error)) {
+               ftd->success = FALSE;
+               return;
+       }
+
+       ftd->success = e_cal_client_get_timezone_sync (ftd->from_client, tzid, &tz, ftd->cancellable, 
ftd->error);
+       if (ftd->success && tz != NULL)
+               ftd->success = e_cal_client_add_timezone_sync (ftd->to_client, tz, ftd->cancellable, 
ftd->error);
+}
+
+static void
+copy_source_thread (EAlertSinkThreadJobData *job_data,
+                   gpointer user_data,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+       CopySourceData *csd = user_data;
+       EClient *client;
+       ECalClient *from_client = NULL, *to_client = NULL;
+       GSList *objects = NULL, *link;
+       struct ForeachTzidData ftd;
+       gint n_objects, ii, last_percent = 0;
+
+       if (!csd)
+               goto out;
+
+       client = e_util_open_client_sync (job_data, e_cal_model_get_client_cache (csd->model), 
csd->extension_name, csd->from_source, 30, cancellable, error);
+       if (client)
+               from_client = E_CAL_CLIENT (client);
+
+       if (!from_client)
+               goto out;
+
+       client = e_util_open_client_sync (job_data, e_cal_model_get_client_cache (csd->model), 
csd->extension_name, csd->to_source, 30, cancellable, error);
+       if (client)
+               to_client = E_CAL_CLIENT (client);
+
+       if (!to_client)
+               goto out;
+
+       if (e_client_is_readonly (E_CLIENT (to_client))) {
+               g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_READ_ONLY, _("Destination is read only"));
+               goto out;
+       }
+
+       if (!e_cal_client_get_object_list_sync (from_client, "#t", &objects, cancellable, error))
+               goto out;
+
+       ftd.from_client = from_client;
+       ftd.to_client = to_client;
+       ftd.success = TRUE;
+       ftd.cancellable = cancellable;
+       ftd.error = error;
+
+       n_objects = g_slist_length (objects);
+
+       for (link = objects, ii = 0; link && ftd.success && !g_cancellable_is_cancelled (cancellable); link = 
g_slist_next (link), ii++) {
+               icalcomponent *icalcomp = link->data;
+               icalcomponent *existing_icalcomp = NULL;
+               gint percent = 100 * (ii + 1) / n_objects;
+               GError *local_error = NULL;
+
+               if (e_cal_client_get_object_sync (to_client, icalcomponent_get_uid (icalcomp), NULL, 
&existing_icalcomp, cancellable, &local_error) &&
+                   icalcomp != NULL) {
+                       if (!e_cal_client_modify_object_sync (to_client, icalcomp, E_CAL_OBJ_MOD_ALL, 
cancellable, error))
+                               break;
+
+                       icalcomponent_free (existing_icalcomp);
+               } else if (local_error && !g_error_matches (local_error, E_CAL_CLIENT_ERROR, 
E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND)) {
+                       g_propagate_error (error, local_error);
+                       break;
+               } else {
+                       icalcomponent_foreach_tzid (icalcomp, add_timezone_to_cal_cb, &ftd);
+
+                       g_clear_error (&local_error);
+
+                       if (!ftd.success)
+                               break;
+
+                       if (!e_cal_client_create_object_sync (to_client, icalcomp, NULL, cancellable, error))
+                               break;
+               }
+
+               if (percent != last_percent) {
+                       camel_operation_progress (cancellable, percent);
+                       last_percent = percent;
+               }
+       }
+
+       if (ii > 0 && ftd.success)
+               csd->to_client = g_object_ref (to_client);
+ out:
+       e_cal_client_free_icalcomp_slist (objects);
+       g_clear_object (&from_client);
+       g_clear_object (&to_client);
+}
+
+/**
+ * e_cal_dialogs_copy_source
+ *
+ * Implements the Copy command for sources, allowing the user to select a target
+ * source to copy to.
+ */
+void
+e_cal_dialogs_copy_source (GtkWindow *parent,
+                          ECalModel *model,
+                          ESource *from_source)
+{
+       ECalClientSourceType obj_type;
+       ESource *to_source;
+       const gchar *extension_name;
+       const gchar *format;
+       const gchar *alert_ident;
+
+       g_return_if_fail (E_IS_CAL_MODEL (model));
+       g_return_if_fail (E_IS_SOURCE (from_source));
+
+       switch (e_cal_model_get_component_kind (model)) {
+               case ICAL_VEVENT_COMPONENT:
+                       obj_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+                       extension_name = E_SOURCE_EXTENSION_CALENDAR;
+                       format = _("Copying events to the calendar '%s'");
+                       alert_ident = "calendar:failed-copy-event";
+                       break;
+               case ICAL_VJOURNAL_COMPONENT:
+                       obj_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
+                       extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+                       format = _("Copying memos to the memo list '%s'");
+                       alert_ident = "calendar:failed-copy-memo";
+                       break;
+               case ICAL_VTODO_COMPONENT:
+                       obj_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
+                       extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+                       format = _("Copying tasks to the task list '%s'");
+                       alert_ident = "calendar:failed-copy-task";
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+       }
+
+       to_source = e_cal_dialogs_select_source (parent, e_cal_model_get_registry (model), obj_type, 
from_source);
+       if (to_source) {
+               CopySourceData *csd;
+               GCancellable *cancellable;
+               ECalDataModel *data_model;
+               gchar *display_name;
+               gchar *description;
+
+               csd = g_new0 (CopySourceData, 1);
+               csd->model = g_object_ref (model);
+               csd->from_source = g_object_ref (from_source);
+               csd->to_source = g_object_ref (to_source);
+               csd->to_client = NULL;
+               csd->extension_name = extension_name;
+
+               display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), to_source);
+               description = g_strdup_printf (format, display_name);
+               data_model = e_cal_model_get_data_model (model);
+
+               cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident, 
display_name,
+                       copy_source_thread, csd, copy_source_data_free);
+
+               g_clear_object (&cancellable);
+               g_free (display_name);
+               g_free (description);
+       }
+
+       g_clear_object (&to_source);
+}
+
+/**
+ * e_cal_dialogs_delete_component:
+ * @comp: A calendar component if a single component is to be deleted, or NULL
+ * if more that one component is to be deleted.
+ * @consider_as_untitled: If deleting more than one component, this is ignored.
+ * Otherwise, whether to consider the component as not having a summary; if
+ * FALSE then the component's summary string will be used.
+ * @n_comps: Number of components that are to be deleted.
+ * @vtype: Type of the components that are to be deleted.  This is ignored
+ * if only one component is to be deleted, and the vtype is extracted from
+ * the component instead.
+ * @widget: A widget to use as a basis for conversion from UTF8 into font
+ * encoding.
+ *
+ * Pops up a dialog box asking the user whether he wants to delete a number of
+ * calendar components.  The dialog will not appear, however, if the
+ * configuration option for confirmation is turned off.
+ *
+ * Return value: TRUE if the user clicked Yes, FALSE otherwise.  If the
+ * configuration option for confirmation is turned off, this function will
+ * unconditionally return TRUE.
+ **/
+gboolean
+e_cal_dialogs_delete_component (ECalComponent *comp,
+                               gboolean consider_as_untitled,
+                               gint n_comps,
+                               ECalComponentVType vtype,
+                               GtkWidget *widget)
+{
+       const gchar *id;
+       gchar *arg0 = NULL;
+       gint response;
+       gboolean attendees;
+
+       if (comp) {
+               g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+               g_return_val_if_fail (n_comps == 1, FALSE);
+       } else {
+               g_return_val_if_fail (n_comps > 1, FALSE);
+               g_return_val_if_fail (vtype != E_CAL_COMPONENT_NO_TYPE, FALSE);
+       }
+
+       g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+       if (comp) {
+               ECalComponentText summary;
+
+               vtype = e_cal_component_get_vtype (comp);
+
+               if (!consider_as_untitled) {
+                       e_cal_component_get_summary (comp, &summary);
+                       arg0 = g_strdup (summary.value);
+               }
+
+               switch (vtype) {
+               case E_CAL_COMPONENT_EVENT:
+                       attendees = e_cal_component_has_attendees (comp);
+                       if (arg0) {
+                               if (attendees)
+                                       id = "calendar:prompt-delete-titled-meeting";
+                               else
+                                       id = "calendar:prompt-delete-titled-appointment";
+                       } else {
+                               if (attendees)
+                                       id = "calendar:prompt-delete-meeting";
+                               else
+                                       id = "calendar:prompt-delete-appointment";
+                       }
+                       break;
+
+               case E_CAL_COMPONENT_TODO:
+                       if (arg0)
+                               id = "calendar:prompt-delete-named-task";
+                       else
+                               id = "calendar:prompt-delete-task";
+                       break;
+
+               case E_CAL_COMPONENT_JOURNAL:
+                       if (arg0)
+                               id = "calendar:prompt-delete-named-memo";
+                       else
+                               id = "calendar:prompt-delete-memo";
+                       break;
+
+               default:
+                       g_message (
+                               "delete_component_dialog(): Cannot handle object of type %d",
+                               vtype);
+                       g_free (arg0);
+                       return FALSE;
+               }
+       } else {
+               switch (vtype) {
+               case E_CAL_COMPONENT_EVENT:
+                       if (n_comps == 1)
+                               id = "calendar:prompt-delete-appointment";
+                       else
+                               id = "calendar:prompt-delete-appointments";
+                       break;
+
+               case E_CAL_COMPONENT_TODO:
+                       if (n_comps == 1)
+                               id = "calendar:prompt-delete-task";
+                       else
+                               id = "calendar:prompt-delete-tasks";
+                       break;
+
+               case E_CAL_COMPONENT_JOURNAL:
+                       if (n_comps == 1)
+                               id = "calendar:prompt-delete-memo";
+                       else
+                               id = "calendar:prompt-delete-memos";
+                       break;
+
+               default:
+                       g_message (
+                               "delete_component_dialog(): Cannot handle objects of type %d",
+                               vtype);
+                       return FALSE;
+               }
+
+               if (n_comps > 1)
+                       arg0 = g_strdup_printf ("%d", n_comps);
+       }
+
+       response = e_alert_run_dialog_for_args ((GtkWindow *) gtk_widget_get_toplevel (widget), id, arg0, 
NULL);
+       g_free (arg0);
+
+       return response == GTK_RESPONSE_YES;
+}
+
+static void
+cb_toggled_cb (GtkToggleButton *toggle,
+               gpointer data)
+{
+       gboolean active = FALSE;
+       GtkWidget *entry = (GtkWidget *) data;
+
+       active = gtk_toggle_button_get_active (toggle);
+       gtk_widget_set_sensitive (entry, active);
+}
+
+gboolean
+e_cal_dialogs_prompt_retract (GtkWidget *parent,
+                             ECalComponent *comp,
+                             gchar **retract_text,
+                             gboolean *retract)
+{
+       gchar *message = NULL;
+       ECalComponentVType type = E_CAL_COMPONENT_NO_TYPE;
+       GtkMessageDialog *dialog = NULL;
+       GtkWidget *cb, *label, *entry, *vbox, *sw, *frame;
+       gboolean ret_val = FALSE;
+
+       type = e_cal_component_get_vtype (comp);
+
+       switch (type) {
+               case E_CAL_COMPONENT_EVENT:
+                       message = g_strdup_printf (_("Are you sure you want to delete this meeting?"));
+                       break;
+               case E_CAL_COMPONENT_TODO:
+                       message = g_strdup_printf (_("Are you sure you want to delete this task?"));
+                       break;
+               case E_CAL_COMPONENT_JOURNAL:
+                       message = g_strdup_printf (_("Are you sure you want to delete this memo?"));
+                       break;
+               default:
+                       g_message ("Retract: Unsupported object type \n");
+                       return FALSE;
+       }
+
+       dialog = (GtkMessageDialog *) gtk_message_dialog_new_with_markup
+               ((GtkWindow *) gtk_widget_get_toplevel (parent), GTK_DIALOG_MODAL,
+                GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "<b>%s</b>", message);
+       g_free (message);
+
+       gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+       vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+       gtk_box_set_spacing (GTK_BOX (vbox), 12);
+
+       cb = gtk_check_button_new_with_mnemonic (_("_Delete this item from all other recipient's 
mailboxes?"));
+       gtk_container_add (GTK_CONTAINER (vbox), cb);
+
+       label = gtk_label_new_with_mnemonic (_("_Retract comment"));
+
+       frame = gtk_frame_new (NULL);
+       gtk_frame_set_label_widget ((GtkFrame *) frame, label);
+       gtk_frame_set_label_align ((GtkFrame *) frame, 0, 0);
+       gtk_container_add (GTK_CONTAINER (vbox), frame);
+       gtk_frame_set_shadow_type ((GtkFrame *) frame, GTK_SHADOW_NONE);
+
+       sw = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy ((GtkScrolledWindow *) sw, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+       entry = gtk_text_view_new ();
+       gtk_scrolled_window_add_with_viewport ((GtkScrolledWindow *) sw, entry);
+       gtk_label_set_mnemonic_widget ((GtkLabel *) label, entry);
+       gtk_container_add (GTK_CONTAINER (frame), sw);
+
+       g_signal_connect (
+               cb, "toggled",
+               G_CALLBACK (cb_toggled_cb), entry);
+
+       gtk_widget_show_all ((GtkWidget *) dialog);
+
+       ret_val = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+       if (ret_val) {
+               if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (cb))) {
+                       GtkTextIter text_iter_start, text_iter_end;
+                       GtkTextBuffer *text_buffer;
+
+                       *retract = TRUE;
+                       text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
+                       gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start);
+                       gtk_text_buffer_get_end_iter   (text_buffer, &text_iter_end);
+
+                       *retract_text = gtk_text_buffer_get_text (text_buffer, &text_iter_start,
+                                       &text_iter_end, FALSE);
+               } else
+                       *retract = FALSE;
+       }
+
+       gtk_widget_destroy ((GtkWidget *) dialog);
+
+       return ret_val;
+}
+
+typedef struct {
+       GtkWidget *dialog;
+
+       GtkWidget *month_combobox;
+       GtkWidget *year;
+       ECalendar *ecal;
+       GtkWidget *grid;
+
+       gint year_val;
+       gint month_val;
+       gint day_val;
+
+       ETagCalendar *tag_calendar;
+
+       ECalDataModel *data_model;
+       ECalendarViewMoveType *out_move_type;
+       time_t *out_exact_date;
+} GoToDialog;
+
+static GoToDialog *dlg = NULL;
+
+/* Callback used when the year adjustment is changed */
+static void
+year_changed (GtkAdjustment *adj,
+              gpointer data)
+{
+       GtkSpinButton *spin_button;
+       GoToDialog *dlg = data;
+
+       spin_button = GTK_SPIN_BUTTON (dlg->year);
+       dlg->year_val = gtk_spin_button_get_value_as_int (spin_button);
+
+       e_calendar_item_set_first_month (
+               dlg->ecal->calitem, dlg->year_val, dlg->month_val);
+}
+
+/* Callback used when a month button is toggled */
+static void
+month_changed (GtkToggleButton *toggle,
+               gpointer data)
+{
+       GtkComboBox *combo_box;
+       GoToDialog *dlg = data;
+
+       combo_box = GTK_COMBO_BOX (dlg->month_combobox);
+       dlg->month_val = gtk_combo_box_get_active (combo_box);
+
+       e_calendar_item_set_first_month (
+               dlg->ecal->calitem, dlg->year_val, dlg->month_val);
+}
+
+/* Event handler for day groups in the month item.  A button press makes
+ * the calendar jump to the selected day and destroys the Go-to dialog box. */
+static void
+ecal_event (ECalendarItem *calitem,
+            gpointer user_data)
+{
+       GoToDialog *dlg = user_data;
+       GDate start_date, end_date;
+       struct icaltimetype tt = icaltime_null_time ();
+       icaltimezone *timezone;
+       time_t et;
+
+       e_calendar_item_get_selection (calitem, &start_date, &end_date);
+       timezone = e_cal_data_model_get_timezone (dlg->data_model);
+
+       tt.year = g_date_get_year (&start_date);
+       tt.month = g_date_get_month (&start_date);
+       tt.day = g_date_get_day (&start_date);
+
+       et = icaltime_as_timet_with_zone (tt, timezone);
+
+       *(dlg->out_move_type) = E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY;
+       *(dlg->out_exact_date) = et;
+
+       gtk_dialog_response (GTK_DIALOG (dlg->dialog), GTK_RESPONSE_APPLY);
+}
+
+/* Returns the current time, for the ECalendarItem. */
+static struct tm
+get_current_time (ECalendarItem *calitem,
+                  gpointer data)
+{
+       icaltimezone *zone;
+       struct tm tmp_tm = { 0 };
+       struct icaltimetype tt;
+
+       /* Get the current timezone. */
+       zone = calendar_config_get_icaltimezone ();
+
+       tt = icaltime_from_timet_with_zone (time (NULL), FALSE, zone);
+
+       /* Now copy it to the struct tm and return it. */
+       tmp_tm.tm_year = tt.year - 1900;
+       tmp_tm.tm_mon = tt.month - 1;
+       tmp_tm.tm_mday = tt.day;
+       tmp_tm.tm_hour = tt.hour;
+       tmp_tm.tm_min = tt.minute;
+       tmp_tm.tm_sec = tt.second;
+       tmp_tm.tm_isdst = -1;
+
+       return tmp_tm;
+}
+
+/* Gets the widgets from the XML file and returns if they are all available. */
+static void
+goto_dialog_create_widgets (GoToDialog *dlg,
+                           GtkWindow *parent)
+{
+       ECalendarItem *calitem;
+       GtkWidget *widget;
+       GtkGrid *grid;
+       GtkComboBoxText *text_combo;
+
+       dlg->dialog = gtk_dialog_new_with_buttons (_("Select Date"), parent, 0,
+               _("Select _Today"), GTK_RESPONSE_ACCEPT,
+               _("_Cancel"), GTK_RESPONSE_CANCEL,
+               NULL);
+
+       g_object_set (G_OBJECT (dlg->dialog),
+               "border-width", 12,
+               NULL);
+
+       widget = gtk_grid_new ();
+       dlg->grid = widget;
+       grid = GTK_GRID (widget);
+
+       widget = gtk_dialog_get_content_area (GTK_DIALOG (dlg->dialog));
+       gtk_box_pack_start (GTK_BOX (widget), dlg->grid, TRUE, TRUE, 0);
+
+       widget = gtk_combo_box_text_new ();
+       dlg->month_combobox = widget;
+
+       text_combo = GTK_COMBO_BOX_TEXT (widget);
+       gtk_combo_box_text_append_text (text_combo, _("January"));
+       gtk_combo_box_text_append_text (text_combo, _("February"));
+       gtk_combo_box_text_append_text (text_combo, _("March"));
+       gtk_combo_box_text_append_text (text_combo, _("April"));
+       gtk_combo_box_text_append_text (text_combo, _("May"));
+       gtk_combo_box_text_append_text (text_combo, _("June"));
+       gtk_combo_box_text_append_text (text_combo, _("July"));
+       gtk_combo_box_text_append_text (text_combo, _("August"));
+       gtk_combo_box_text_append_text (text_combo, _("September"));
+       gtk_combo_box_text_append_text (text_combo, _("October"));
+       gtk_combo_box_text_append_text (text_combo, _("November"));
+       gtk_combo_box_text_append_text (text_combo, _("December"));
+
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       widget = gtk_spin_button_new (NULL, 1, 0);
+       gtk_spin_button_set_range (GTK_SPIN_BUTTON (widget), 1969, 9999);
+       gtk_spin_button_set_increments (GTK_SPIN_BUTTON (widget), 1, 5);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+
+       dlg->year = widget;
+
+       dlg->ecal = E_CALENDAR (e_calendar_new ());
+       dlg->tag_calendar = e_tag_calendar_new (dlg->ecal);
+
+       calitem = dlg->ecal->calitem;
+
+       gnome_canvas_item_set (
+               GNOME_CANVAS_ITEM (calitem),
+               "move_selection_when_moving", FALSE,
+               NULL);
+       e_calendar_item_set_display_popup (calitem, FALSE);
+       g_object_set (G_OBJECT (dlg->ecal),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+
+       gtk_grid_attach (grid, GTK_WIDGET (dlg->ecal), 0, 1, 2, 1);
+
+       e_calendar_item_set_first_month (calitem, dlg->year_val, dlg->month_val);
+       e_calendar_item_set_get_time_callback (calitem, get_current_time, dlg, NULL);
+
+       gtk_widget_show_all (GTK_WIDGET (grid));
+}
+
+/* Create a copy, thus a move to a distant date will not cause large event lookups */
+
+/* Creates a "goto date" dialog and runs it */
+gboolean
+e_cal_dialogs_goto_run (GtkWindow *parent,
+                       ECalDataModel *data_model,
+                       const GDate *from_date,
+                       ECalendarViewMoveType *out_move_type,
+                       time_t *out_exact_date)
+{
+       GtkAdjustment *adj;
+       gint response;
+
+       if (dlg) {
+               return FALSE;
+       }
+
+       g_return_val_if_fail (E_IS_CAL_DATA_MODEL (data_model), FALSE);
+       g_return_val_if_fail (out_move_type != NULL, FALSE);
+       g_return_val_if_fail (out_exact_date != NULL, FALSE);
+
+       dlg = g_new0 (GoToDialog, 1);
+
+       goto_dialog_create_widgets (dlg, parent);
+
+       dlg->data_model = e_cal_data_model_new_clone (data_model);
+       dlg->out_move_type = out_move_type;
+       dlg->out_exact_date = out_exact_date;
+
+       if (from_date) {
+               dlg->year_val = g_date_get_year (from_date);
+               dlg->month_val = g_date_get_month (from_date) - 1;
+               dlg->day_val = g_date_get_day (from_date);
+       } else {
+               struct icaltimetype tt;
+               icaltimezone *timezone;
+
+               timezone = e_cal_data_model_get_timezone (dlg->data_model);
+               tt = icaltime_current_time_with_zone (timezone);
+
+               dlg->year_val = tt.year;
+               dlg->month_val = tt.month - 1;
+               dlg->day_val = tt.day;
+       }
+
+       g_signal_connect (
+               dlg->month_combobox, "changed",
+               G_CALLBACK (month_changed), dlg);
+
+       adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dlg->year));
+       g_signal_connect (
+               adj, "value_changed",
+               G_CALLBACK (year_changed), dlg);
+
+       g_signal_connect (
+               dlg->ecal->calitem, "selection_changed",
+               G_CALLBACK (ecal_event), dlg);
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (dlg->month_combobox), dlg->month_val);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (dlg->year), dlg->year_val);
+
+       gtk_window_set_transient_for (GTK_WINDOW (dlg->dialog), parent);
+
+       /* set initial selection to current day */
+
+       dlg->ecal->calitem->selection_set = TRUE;
+       dlg->ecal->calitem->selection_start_month_offset = 0;
+       dlg->ecal->calitem->selection_start_day = dlg->day_val;
+       dlg->ecal->calitem->selection_end_month_offset = 0;
+       dlg->ecal->calitem->selection_end_day = dlg->day_val;
+
+       gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (dlg->ecal->calitem));
+
+       e_tag_calendar_subscribe (dlg->tag_calendar, dlg->data_model);
+
+       response = gtk_dialog_run (GTK_DIALOG (dlg->dialog));
+
+       e_tag_calendar_unsubscribe (dlg->tag_calendar, dlg->data_model);
+
+       gtk_widget_destroy (dlg->dialog);
+
+       if (response == GTK_RESPONSE_ACCEPT)
+               *(dlg->out_move_type) = E_CALENDAR_VIEW_MOVE_TO_TODAY;
+
+       g_clear_object (&dlg->tag_calendar);
+       g_clear_object (&dlg->data_model);
+
+       g_free (dlg);
+       dlg = NULL;
+
+       return response == GTK_RESPONSE_ACCEPT || response == GTK_RESPONSE_APPLY;
+}
+
+gboolean
+e_cal_dialogs_recur_component (ECalClient *client,
+                              ECalComponent *comp,
+                              ECalObjModType *mod,
+                              GtkWindow *parent,
+                              gboolean delegated)
+{
+       gchar *str;
+       GtkWidget *dialog, *rb_this, *rb_prior, *rb_future, *rb_all, *hbox;
+       GtkWidget *placeholder, *vbox;
+       GtkWidget *content_area;
+       ECalComponentVType vtype;
+       gboolean ret;
+
+       g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+
+       vtype = e_cal_component_get_vtype (comp);
+
+       switch (vtype) {
+       case E_CAL_COMPONENT_EVENT:
+               if (!delegated)
+                       str = g_strdup_printf (_("You are modifying a recurring event. What would you like to 
modify?"));
+               else
+                       str = g_strdup_printf (_("You are delegating a recurring event. What would you like 
to delegate?"));
+               break;
+
+       case E_CAL_COMPONENT_TODO:
+               str = g_strdup_printf (_("You are modifying a recurring task. What would you like to 
modify?"));
+               break;
+
+       case E_CAL_COMPONENT_JOURNAL:
+               str = g_strdup_printf (_("You are modifying a recurring memo. What would you like to 
modify?"));
+               break;
+
+       default:
+               g_message ("recur_component_dialog(): Cannot handle object of type %d", vtype);
+               return FALSE;
+       }
+
+       dialog = gtk_message_dialog_new (parent, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "%s", str);
+       g_free (str);
+       gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+       content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+       gtk_container_add (GTK_CONTAINER (content_area), hbox);
+
+       placeholder = gtk_label_new ("");
+       gtk_widget_set_size_request (placeholder, 48, 48);
+       gtk_box_pack_start (GTK_BOX (hbox), placeholder, FALSE, FALSE, 0);
+       gtk_widget_show (placeholder);
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+       gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+       gtk_widget_show (vbox);
+
+       rb_this = gtk_radio_button_new_with_label (NULL, _("This Instance Only"));
+       gtk_container_add (GTK_CONTAINER (vbox), rb_this);
+
+       if (!e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_THISANDPRIOR)) {
+               rb_prior = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this), _("This 
and Prior Instances"));
+               gtk_container_add (GTK_CONTAINER (vbox), rb_prior);
+       } else
+               rb_prior = NULL;
+
+       if (!e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_NO_THISANDFUTURE)) {
+               rb_future = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this), _("This 
and Future Instances"));
+               gtk_container_add (GTK_CONTAINER (vbox), rb_future);
+       } else
+               rb_future = NULL;
+
+       rb_all = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (rb_this), _("All Instances"));
+       gtk_container_add (GTK_CONTAINER (vbox), rb_all);
+
+       gtk_widget_show_all (hbox);
+
+       placeholder = gtk_label_new ("");
+       gtk_box_pack_start (GTK_BOX (content_area), placeholder, FALSE, FALSE, 0);
+       gtk_widget_show (placeholder);
+
+       ret = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK;
+
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_this)))
+               *mod = E_CAL_OBJ_MOD_THIS;
+       else if (rb_prior && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_prior)))
+               *mod = E_CAL_OBJ_MOD_THIS_AND_PRIOR;
+       else if (rb_future && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_future)))
+               *mod = E_CAL_OBJ_MOD_THIS_AND_FUTURE;
+       else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb_all))) {
+               *mod = E_CAL_OBJ_MOD_ALL;
+       }
+
+       gtk_widget_destroy (dialog);
+
+       return ret;
+}
+
+gboolean
+e_cal_dialogs_recur_icalcomp (ECalClient *client,
+                             icalcomponent *icalcomp,
+                             ECalObjModType *mod,
+                             GtkWindow *parent,
+                             gboolean delegated)
+{
+       ECalComponent *comp;
+       gboolean res;
+
+       g_return_val_if_fail (icalcomp != NULL, FALSE);
+
+       comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp));
+       if (!comp)
+               return FALSE;
+
+       if (!e_cal_component_is_instance (comp)) {
+               *mod = E_CAL_OBJ_MOD_ALL;
+               g_object_unref (comp);
+
+               return TRUE;
+       }
+
+       res = e_cal_dialogs_recur_component (client, comp, mod, parent, delegated);
+
+       g_object_unref (comp);
+
+       return res;
+}
+
+/**
+ * e_cal_dialogs_select_source
+ *
+ * Implements dialog for allowing user to select a destination source.
+ */
+ESource *
+e_cal_dialogs_select_source (GtkWindow *parent,
+                            ESourceRegistry *registry,
+                            ECalClientSourceType obj_type,
+                            ESource *except_source)
+{
+       GtkWidget *dialog;
+       ESource *selected_source = NULL;
+       const gchar *extension_name;
+       const gchar *icon_name;
+
+       g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+       if (obj_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
+               extension_name = E_SOURCE_EXTENSION_CALENDAR;
+               icon_name = "x-office-calendar";
+       } else if (obj_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS) {
+               extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+               icon_name = "stock_todo";
+       } else if (obj_type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+               extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+               icon_name = "stock_journal";
+       } else
+               return NULL;
+
+       /* create the dialog */
+       dialog = e_source_selector_dialog_new (parent, registry, extension_name);
+
+       if (icon_name)
+               gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name);
+
+       if (except_source)
+               e_source_selector_dialog_set_except_source (E_SOURCE_SELECTOR_DIALOG (dialog), except_source);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK)
+               goto exit;
+
+       selected_source = e_source_selector_dialog_peek_primary_selection (
+               E_SOURCE_SELECTOR_DIALOG (dialog));
+       if (selected_source != NULL)
+               g_object_ref (selected_source);
+
+ exit:
+       gtk_widget_destroy (dialog);
+
+       return selected_source;
+}
+
+static gboolean
+component_has_new_attendees (ECalComponent *comp)
+{
+       g_return_val_if_fail (comp != NULL, FALSE);
+
+       if (!e_cal_component_has_attendees (comp))
+               return FALSE;
+
+       return g_object_get_data (G_OBJECT (comp), "new-attendees") != NULL;
+}
+
+static gboolean
+have_nonprocedural_alarm (ECalComponent *comp)
+{
+       GList *uids, *l;
+
+       g_return_val_if_fail (comp != NULL, FALSE);
+
+       uids = e_cal_component_get_alarm_uids (comp);
+
+       for (l = uids; l; l = l->next) {
+               ECalComponentAlarm *alarm;
+               ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN;
+
+               alarm = e_cal_component_get_alarm (comp, (const gchar *) l->data);
+               if (alarm) {
+                       e_cal_component_alarm_get_action (alarm, &action);
+                       e_cal_component_alarm_free (alarm);
+
+                       if (action != E_CAL_COMPONENT_ALARM_NONE &&
+                           action != E_CAL_COMPONENT_ALARM_PROCEDURE &&
+                           action != E_CAL_COMPONENT_ALARM_UNKNOWN) {
+                               cal_obj_uid_list_free (uids);
+                               return TRUE;
+                       }
+               }
+       }
+
+       cal_obj_uid_list_free (uids);
+
+       return FALSE;
+}
+
+static GtkWidget *
+add_checkbox (GtkBox *where,
+              const gchar *caption)
+{
+       GtkWidget *checkbox, *align;
+
+       g_return_val_if_fail (where != NULL, NULL);
+       g_return_val_if_fail (caption != NULL, NULL);
+
+       checkbox = gtk_check_button_new_with_mnemonic (caption);
+       align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
+       gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 12);
+       gtk_container_add (GTK_CONTAINER (align), checkbox);
+       gtk_widget_show (checkbox);
+       gtk_box_pack_start (where, align, TRUE, TRUE, 2);
+       gtk_widget_show (align);
+
+       return checkbox;
+}
+
+/**
+ * e_cal_dialogs_send_component:
+ *
+ * Pops up a dialog box asking the user whether he wants to send a
+ * iTip/iMip message
+ *
+ * Return value: TRUE if the user clicked Yes, FALSE otherwise.
+ **/
+gboolean
+e_cal_dialogs_send_component (GtkWindow *parent,
+                             ECalClient *client,
+                             ECalComponent *comp,
+                             gboolean new,
+                             gboolean *strip_alarms,
+                             gboolean *only_new_attendees)
+{
+       ECalComponentVType vtype;
+       const gchar *id;
+       GtkWidget *dialog, *sa_checkbox = NULL, *ona_checkbox = NULL;
+       GtkWidget *content_area;
+       gboolean res;
+
+       if (strip_alarms)
+               *strip_alarms = TRUE;
+
+       if (e_cal_client_check_save_schedules (client))
+               return FALSE;
+
+       if (!itip_component_has_recipients (comp))
+               return FALSE;
+
+       vtype = e_cal_component_get_vtype (comp);
+
+       switch (vtype) {
+       case E_CAL_COMPONENT_EVENT:
+               if (new)
+                       id = "calendar:prompt-meeting-invite";
+               else
+                       id = "calendar:prompt-send-updated-meeting-info";
+               break;
+
+       case E_CAL_COMPONENT_TODO:
+               if (new)
+                       id = "calendar:prompt-send-task";
+               else
+                       id = "calendar:prompt-send-updated-task-info";
+               break;
+       case E_CAL_COMPONENT_JOURNAL:
+               if (new)
+                       id = "calendar:prompt-send-memo";
+               else
+                       id = "calendar:prompt-send-updated-memo-info";
+               break;
+       default:
+               g_message (
+                       "send_component_dialog(): "
+                       "Cannot handle object of type %d", vtype);
+               return FALSE;
+       }
+
+       if (only_new_attendees && !component_has_new_attendees (comp)) {
+               /* do not show the check if there is no new attendee and
+                * set as all attendees are required to be notified */
+               *only_new_attendees = FALSE;
+
+               /* pretend it as being passed NULL to simplify code below */
+               only_new_attendees = NULL;
+       }
+
+       if (strip_alarms && !have_nonprocedural_alarm (comp)) {
+               /* pretend it as being passed NULL to simplify code below */
+               strip_alarms = NULL;
+       }
+
+       dialog = e_alert_dialog_new_for_args (parent, id, NULL);
+       content_area = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
+
+       if (strip_alarms)
+               sa_checkbox = add_checkbox (GTK_BOX (content_area), _("Send my reminders with this event"));
+       if (only_new_attendees)
+               ona_checkbox = add_checkbox (GTK_BOX (content_area), _("Notify new attendees _only"));
+
+       res = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
+
+       if (res && strip_alarms)
+               *strip_alarms = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sa_checkbox));
+       if (only_new_attendees)
+               *only_new_attendees = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ona_checkbox));
+
+       gtk_widget_destroy (GTK_WIDGET (dialog));
+
+       return res;
+}
+
+/**
+ * e_cal_dialogs_send_dragged_or_resized_component:
+ *
+ * Pops up a dialog box asking the user whether he wants to send a
+ * iTip/iMip message or cancel the drag/resize operations
+ *
+ * Return value: GTK_RESPONSE_YES if the user clicked Yes,
+ *              GTK_RESPONSE_NO if the user clicked No and
+ *              GTK_RESPONSE_CANCEL otherwise.
+ **/
+GtkResponseType
+e_cal_dialogs_send_dragged_or_resized_component (GtkWindow *parent,
+                                                ECalClient *client,
+                                                ECalComponent *comp,
+                                                gboolean *strip_alarms,
+                                                gboolean *only_new_attendees)
+{
+       ECalComponentVType vtype;
+       const gchar *id;
+       GtkWidget *dialog, *sa_checkbox = NULL, *ona_checkbox = NULL;
+       GtkWidget *content_area;
+       gboolean save_schedules = FALSE;
+       GtkResponseType res;
+
+       if (strip_alarms)
+               *strip_alarms = TRUE;
+
+       if (e_cal_client_check_save_schedules (client))
+               save_schedules = TRUE;
+
+       if (!itip_component_has_recipients (comp))
+               save_schedules = TRUE;
+
+       vtype = e_cal_component_get_vtype (comp);
+
+       switch (vtype) {
+       case E_CAL_COMPONENT_EVENT:
+               id = save_schedules ?
+                       "calendar:prompt-save-meeting-dragged-or-resized" :
+                       "calendar:prompt-send-updated-meeting-info-dragged-or-resized";
+               break;
+       default:
+               g_message (
+                       "send_component_dialog(): "
+                       "Cannot handle object of type %d", vtype);
+               return GTK_RESPONSE_CANCEL;
+       }
+
+       if (only_new_attendees && !component_has_new_attendees (comp)) {
+               /* do not show the check if there is no new attendee and
+                * set as all attendees are required to be notified */
+               *only_new_attendees = FALSE;
+
+               /* pretend it as being passed NULL to simplify code below */
+               only_new_attendees = NULL;
+       }
+
+       if (strip_alarms && !have_nonprocedural_alarm (comp)) {
+               /* pretend it as being passed NULL to simplify code below */
+               strip_alarms = NULL;
+       }
+
+       dialog = e_alert_dialog_new_for_args (parent, id, NULL);
+       content_area = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));
+
+       if (strip_alarms)
+               sa_checkbox = add_checkbox (GTK_BOX (content_area), _("Send my reminders with this event"));
+       if (only_new_attendees)
+               ona_checkbox = add_checkbox (GTK_BOX (content_area), _("Notify new attendees _only"));
+
+       res = gtk_dialog_run (GTK_DIALOG (dialog));
+
+       /*
+        * When the Escape key is pressed a GTK_RESPONSE_DELETE_EVENT is generated.
+        * We should treat this event as the user cancelling the operation
+        */
+       if (res == GTK_RESPONSE_DELETE_EVENT)
+               res = GTK_RESPONSE_CANCEL;
+
+       if (res == GTK_RESPONSE_YES && strip_alarms)
+               *strip_alarms = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sa_checkbox));
+       if (only_new_attendees)
+               *only_new_attendees = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ona_checkbox));
+
+       gtk_widget_destroy (GTK_WIDGET (dialog));
+
+       return res;
+}
+
+gboolean
+e_cal_dialogs_send_component_prompt_subject (GtkWindow *parent,
+                                            icalcomponent *component)
+{
+       icalcomponent_kind kind;
+       const gchar *id;
+
+       kind = icalcomponent_isa (component);
+
+       switch (kind) {
+       case ICAL_VEVENT_COMPONENT:
+               id = "calendar:prompt-save-no-subject-calendar";
+               break;
+
+       case ICAL_VTODO_COMPONENT:
+               id = "calendar:prompt-save-no-subject-task";
+               break;
+       case ICAL_VJOURNAL_COMPONENT:
+               id = "calendar:prompt-send-no-subject-memo";
+               break;
+
+       default:
+               g_message ("%s: Cannot handle object of type %d", G_STRFUNC, kind);
+               return FALSE;
+       }
+
+       if (e_alert_run_dialog_for_args (parent, id, NULL) == GTK_RESPONSE_YES)
+               return TRUE;
+       else
+               return FALSE;
+}
diff --git a/calendar/gui/e-cal-dialogs.h b/calendar/gui/e-cal-dialogs.h
new file mode 100644
index 0000000..10bf449
--- /dev/null
+++ b/calendar/gui/e-cal-dialogs.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_CAL_DIALOGS_H
+#define E_CAL_DIALOGS_H
+
+#include <gtk/gtk.h>
+#include <libecal/libecal.h>
+#include <calendar/gui/e-cal-model.h>
+#include <calendar/gui/e-calendar-view.h>
+
+gboolean       e_cal_dialogs_cancel_component  (GtkWindow *parent,
+                                                ECalClient *cal_client,
+                                                ECalComponent *comp,
+                                                gboolean deleting);
+void           e_cal_dialogs_copy_source       (GtkWindow *parent,
+                                                ECalModel *model,
+                                                ESource *from_source);
+gboolean       e_cal_dialogs_delete_component  (ECalComponent *comp,
+                                                gboolean consider_as_untitled,
+                                                gint n_comps,
+                                                ECalComponentVType vtype,
+                                                GtkWidget *widget);
+gboolean       e_cal_dialogs_prompt_retract    (GtkWidget *parent,
+                                                ECalComponent *comp,
+                                                gchar **retract_text,
+                                                gboolean *retract);
+gboolean       e_cal_dialogs_goto_run          (GtkWindow *parent,
+                                                ECalDataModel *data_model,
+                                                const GDate *from_date,
+                                                ECalendarViewMoveType *out_move_type,
+                                                time_t *out_exact_date);
+gboolean       e_cal_dialogs_recur_component   (ECalClient *client,
+                                                ECalComponent *comp,
+                                                ECalObjModType *mod,
+                                                GtkWindow *parent,
+                                                gboolean delegated);
+gboolean       e_cal_dialogs_recur_icalcomp    (ECalClient *client,
+                                                icalcomponent *icalcomp,
+                                                ECalObjModType *mod,
+                                                GtkWindow *parent,
+                                                gboolean delegated);
+ESource *      e_cal_dialogs_select_source     (GtkWindow *parent,
+                                                ESourceRegistry *registry,
+                                                ECalClientSourceType type,
+                                                ESource *except_source);
+gboolean       e_cal_dialogs_send_component    (GtkWindow *parent,
+                                                ECalClient *client,
+                                                ECalComponent *comp,
+                                                gboolean new,
+                                                gboolean *strip_alarms,
+                                                gboolean *only_new_attendees);
+GtkResponseType        e_cal_dialogs_send_dragged_or_resized_component
+                                               (GtkWindow *parent,
+                                                ECalClient *client,
+                                                ECalComponent *comp,
+                                                gboolean *strip_alarms,
+                                                gboolean *only_new_attendees);
+gboolean       e_cal_dialogs_send_component_prompt_subject
+                                               (GtkWindow *parent,
+                                                icalcomponent *component);
+
+#endif /* E_CAL_DIALOGS_H */
diff --git a/calendar/gui/e-cal-list-view.c b/calendar/gui/e-cal-list-view.c
index 3f51c3f..2d9daa2 100644
--- a/calendar/gui/e-cal-list-view.c
+++ b/calendar/gui/e-cal-list-view.c
@@ -36,11 +36,6 @@
 
 #include "e-cal-model-calendar.h"
 #include "e-cell-date-edit-text.h"
-#include "dialogs/delete-comp.h"
-#include "dialogs/goto-dialog.h"
-#include "dialogs/send-comp.h"
-#include "dialogs/cancel-comp.h"
-#include "dialogs/recur-comp.h"
 #include "comp-util.h"
 #include "itip-utils.h"
 #include "calendar-config.h"
diff --git a/calendar/gui/e-cal-model-calendar.c b/calendar/gui/e-cal-model-calendar.c
index f5b7021..e5e969a 100644
--- a/calendar/gui/e-cal-model-calendar.c
+++ b/calendar/gui/e-cal-model-calendar.c
@@ -31,8 +31,7 @@
 #include "e-cell-date-edit-text.h"
 #include "itip-utils.h"
 #include "misc.h"
-#include "dialogs/recur-comp.h"
-#include "dialogs/send-comp.h"
+#include "e-cal-dialogs.h"
 
 /* Forward Declarations */
 static void    e_cal_model_calendar_table_model_init
@@ -324,7 +323,7 @@ cal_model_calendar_set_value_at (ETableModel *etm,
 
        /* ask about mod type */
        if (e_cal_component_is_instance (comp)) {
-               if (!recur_component_dialog (comp_data->client, comp, &mod, NULL, FALSE)) {
+               if (!e_cal_dialogs_recur_component (comp_data->client, comp, &mod, NULL, FALSE)) {
                        g_object_unref (comp);
                        return;
                }
diff --git a/calendar/gui/e-cal-model.c b/calendar/gui/e-cal-model.c
index 17f9040..8347526 100644
--- a/calendar/gui/e-cal-model.c
+++ b/calendar/gui/e-cal-model.c
@@ -33,10 +33,9 @@
 #include <e-util/e-util.h>
 #include <e-util/e-util-enumtypes.h>
 
-#include "dialogs/recur-comp.h"
-#include "dialogs/send-comp.h"
 #include "comp-util.h"
 #include "e-cal-data-model-subscriber.h"
+#include "e-cal-dialogs.h"
 #include "e-cal-ops.h"
 #include "itip-utils.h"
 #include "misc.h"
@@ -1663,7 +1662,7 @@ cal_model_set_value_at (ETableModel *etm,
                break;
        }
 
-       if (!recur_icalcomp_dialog (comp_data->client, comp_data->icalcomp, &mod, NULL, FALSE))
+       if (!e_cal_dialogs_recur_icalcomp (comp_data->client, comp_data->icalcomp, &mod, NULL, FALSE))
                return;
 
        e_cal_ops_modify_component (model, comp_data->client, comp_data->icalcomp, mod, 
E_CAL_OPS_SEND_FLAG_DONT_SEND);
diff --git a/calendar/gui/e-cal-ops.c b/calendar/gui/e-cal-ops.c
index c78d779..2d4155b 100644
--- a/calendar/gui/e-cal-ops.c
+++ b/calendar/gui/e-cal-ops.c
@@ -26,12 +26,13 @@
 #include <e-util/e-util.h>
 #include <shell/e-shell-view.h>
 
-#include "dialogs/comp-editor.h"
-#include "dialogs/event-editor.h"
-#include "dialogs/memo-editor.h"
-#include "dialogs/task-editor.h"
-#include "dialogs/send-comp.h"
+#include "e-comp-editor.h"
+#include "e-comp-editor-event.h"
+#include "e-comp-editor-memo.h"
+#include "e-comp-editor-task.h"
+#include "e-cal-dialogs.h"
 #include "comp-util.h"
+#include "itip-utils.h"
 
 #include "e-cal-data-model.h"
 
@@ -66,12 +67,12 @@ cal_ops_manage_send_component (ECalModel *model,
                gboolean can_send = (send_flags & E_CAL_OPS_SEND_FLAG_SEND) != 0;
 
                if (!can_send) /* E_CAL_OPS_SEND_FLAG_ASK */
-                       can_send = send_component_dialog (NULL, client, comp,
+                       can_send = e_cal_dialogs_send_component (NULL, client, comp,
                                (send_flags & E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT) != 0,
                                &strip_alarms, &only_new_attendees);
 
                if (can_send)
-                       itip_send_component (model, E_CAL_COMPONENT_METHOD_REQUEST, comp, client,
+                       itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_REQUEST, comp, client,
                                NULL, NULL, NULL, strip_alarms, only_new_attendees, mod == E_CAL_OBJ_MOD_ALL);
        }
 
@@ -1392,13 +1393,13 @@ e_cal_ops_get_default_component (ECalModel *model,
 }
 
 static void
-cal_ops_emit_model_object_created (CompEditor *editor,
+cal_ops_emit_model_object_created (ECompEditor *comp_editor,
                                   ECalModel *model)
 {
-       g_return_if_fail (IS_COMP_EDITOR (editor));
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
        g_return_if_fail (E_IS_CAL_MODEL (model));
 
-       e_cal_model_emit_object_created (model, comp_editor_get_client (editor));
+       e_cal_model_emit_object_created (model, e_comp_editor_get_target_client (comp_editor));
 }
 
 typedef struct
@@ -1431,104 +1432,72 @@ new_component_data_free (gpointer ptr)
        if (ncd) {
                /* successfully opened the default client */
                if (ncd->client && ncd->comp) {
-                       CompEditor *editor = NULL;
-                       CompEditorFlags flags = 0;
+                       ECompEditor *comp_editor;
+                       ECompEditorFlags flags = 0;
 
                        if (ncd->is_new_component) {
-                               flags |= COMP_EDITOR_NEW_ITEM;
-
-                               if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS)
-                                       flags |= COMP_EDITOR_USER_ORG;
+                               flags |= E_COMP_EDITOR_FLAG_IS_NEW;
                        } else {
                                if (e_cal_component_has_attendees (ncd->comp))
                                        ncd->is_assigned = TRUE;
-
-                               if (ncd->is_assigned && e_cal_component_has_organizer (ncd->comp)) {
-                                       ESourceRegistry *registry;
-
-                                       registry = e_cal_model_get_registry (ncd->model);
-                                       if (itip_organizer_is_user (registry, ncd->comp, ncd->client))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS &&
-                                           (flags & COMP_EDITOR_USER_ORG) == 0 &&
-                                           itip_sentby_is_user (registry, ncd->comp, ncd->client))
-                                               flags |= COMP_EDITOR_USER_ORG;
-                               } else {
-                                       flags |= COMP_EDITOR_USER_ORG;
-                               }
                        }
 
                        if (ncd->is_assigned) {
                                if (ncd->is_new_component)
-                                       flags |= COMP_EDITOR_USER_ORG;
-
-                               if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS)
-                                       flags |= COMP_EDITOR_MEETING;
-                               else if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
-                                       flags |= COMP_EDITOR_IS_SHARED;
-                               else if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_TASKS)
-                                       flags |= COMP_EDITOR_IS_ASSIGNED;
+                                       flags |= E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
+
+                               flags |= E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
                        }
 
-                       switch (ncd->source_type) {
-                               case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
-                                       editor = event_editor_new (ncd->client, ncd->shell, flags);
-                                       if (ncd->is_new_component && ncd->model && ncd->dtstart > 0 && 
ncd->dtend > 0) {
-                                               ECalComponentDateTime dt;
-                                               struct icaltimetype itt;
-                                               icaltimezone *zone;
-
-                                               zone = e_cal_model_get_timezone (ncd->model);
-
-                                               dt.value = &itt;
-                                               if (ncd->all_day)
-                                                       dt.tzid = NULL;
-                                               else
-                                                       dt.tzid = icaltimezone_get_tzid (zone);
-
-                                               itt = icaltime_from_timet_with_zone (ncd->dtstart, FALSE, 
zone);
-                                               if (ncd->all_day) {
-                                                       itt.hour = itt.minute = itt.second = 0;
-                                                       itt.is_date = TRUE;
-                                               }
-                                               e_cal_component_set_dtstart (ncd->comp, &dt);
-
-                                               itt = icaltime_from_timet_with_zone (ncd->dtend, FALSE, zone);
-                                               if (ncd->all_day) {
-                                                       /* We round it up to the end of the day, unless it is
-                                                        * already set to midnight */
-                                                       if (itt.hour != 0 || itt.minute != 0 || itt.second != 
0) {
-                                                               icaltime_adjust (&itt, 1, 0, 0, 0);
-                                                       }
-                                                       itt.hour = itt.minute = itt.second = 0;
-                                                       itt.is_date = TRUE;
+                       if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
+                               if (ncd->is_new_component && ncd->model && ncd->dtstart > 0 && ncd->dtend > 
0) {
+                                       ECalComponentDateTime dt;
+                                       struct icaltimetype itt;
+                                       icaltimezone *zone;
+
+                                       zone = e_cal_model_get_timezone (ncd->model);
+
+                                       dt.value = &itt;
+                                       if (ncd->all_day)
+                                               dt.tzid = NULL;
+                                       else
+                                               dt.tzid = icaltimezone_get_tzid (zone);
+
+                                       itt = icaltime_from_timet_with_zone (ncd->dtstart, FALSE, zone);
+                                       if (ncd->all_day) {
+                                               itt.hour = itt.minute = itt.second = 0;
+                                               itt.is_date = TRUE;
+                                       }
+                                       e_cal_component_set_dtstart (ncd->comp, &dt);
+
+                                       itt = icaltime_from_timet_with_zone (ncd->dtend, FALSE, zone);
+                                       if (ncd->all_day) {
+                                               /* We round it up to the end of the day, unless it is
+                                                * already set to midnight */
+                                               if (itt.hour != 0 || itt.minute != 0 || itt.second != 0) {
+                                                       icaltime_adjust (&itt, 1, 0, 0, 0);
                                                }
-                                               e_cal_component_set_dtend (ncd->comp, &dt);
+                                               itt.hour = itt.minute = itt.second = 0;
+                                               itt.is_date = TRUE;
                                        }
-                                       e_cal_component_commit_sequence (ncd->comp);
-                                       break;
-                               case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
-                                       editor = memo_editor_new (ncd->client, ncd->shell, flags);
-                                       break;
-                               case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
-                                       editor = task_editor_new (ncd->client, ncd->shell, flags);
-                                       break;
-                               default:
-                                       g_warn_if_reached ();
-                                       break;
+                                       e_cal_component_set_dtend (ncd->comp, &dt);
+                               }
+                               e_cal_component_commit_sequence (ncd->comp);
                        }
 
-                       if (editor) {
+                       comp_editor = e_comp_editor_open_for_component (NULL, ncd->shell,
+                               ncd->is_new_component ? NULL : e_client_get_source (E_CLIENT (ncd->client)),
+                               e_cal_component_get_icalcomponent (ncd->comp), flags);
+
+                       if (comp_editor) {
                                if (ncd->model) {
-                                       g_signal_connect (editor, "object-created",
+                                       g_signal_connect (comp_editor, "object-created",
                                                G_CALLBACK (cal_ops_emit_model_object_created), ncd->model);
 
-                                       g_object_set_data_full (G_OBJECT (editor), "e-cal-ops-model", 
g_object_ref (ncd->model), g_object_unref);
+                                       g_object_set_data_full (G_OBJECT (comp_editor), "e-cal-ops-model", 
g_object_ref (ncd->model), g_object_unref);
                                }
 
-                               comp_editor_edit_comp (editor, ncd->comp);
-                               gtk_window_present (GTK_WINDOW (editor));
+                               gtk_window_present (GTK_WINDOW (comp_editor));
                        }
                }
 
@@ -1853,15 +1822,15 @@ e_cal_ops_open_component_in_editor_sync (ECalModel *model,
 {
        NewComponentData *ncd;
        ECalComponent *comp;
-       CompEditor *editor;
+       ECompEditor *comp_editor;
 
        g_return_if_fail (E_IS_CAL_MODEL (model));
        g_return_if_fail (E_IS_CAL_CLIENT (client));
        g_return_if_fail (icalcomp != NULL);
 
-       editor = comp_editor_find_instance (icalcomponent_get_uid (icalcomp));
-       if (editor) {
-               gtk_window_present (GTK_WINDOW (editor));
+       comp_editor = e_comp_editor_find_existing_for (e_client_get_source (E_CLIENT (client)), icalcomp);
+       if (comp_editor) {
+               gtk_window_present (GTK_WINDOW (comp_editor));
                return;
        }
 
diff --git a/calendar/gui/e-calendar-view.c b/calendar/gui/e-calendar-view.c
index 402ff40..d61f529 100644
--- a/calendar/gui/e-calendar-view.c
+++ b/calendar/gui/e-calendar-view.c
@@ -34,25 +34,18 @@
 #include <shell/e-shell.h>
 
 #include "comp-util.h"
+#include "ea-cal-view.h"
 #include "ea-calendar.h"
-#include "e-cal-ops.h"
+#include "e-cal-dialogs.h"
+#include "e-cal-list-view.h"
 #include "e-cal-model-calendar.h"
+#include "e-cal-ops.h"
 #include "e-calendar-view.h"
 #include "e-day-view.h"
 #include "e-month-view.h"
-#include "e-cal-list-view.h"
-#include "ea-cal-view.h"
 #include "itip-utils.h"
-#include "dialogs/comp-editor-util.h"
-#include "dialogs/delete-comp.h"
-#include "dialogs/event-editor.h"
-#include "dialogs/send-comp.h"
-#include "dialogs/cancel-comp.h"
-#include "dialogs/recur-comp.h"
-#include "dialogs/select-source-dialog.h"
-#include "dialogs/goto-dialog.h"
-#include "print.h"
 #include "misc.h"
+#include "print.h"
 
 #define E_CALENDAR_VIEW_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -186,7 +179,7 @@ calendar_view_delete_event (ECalendarView *cal_view,
                gchar *retract_comment = NULL;
                gboolean retract = FALSE;
 
-               delete = prompt_retract_dialog (comp, &retract_comment, GTK_WIDGET (cal_view), &retract);
+               delete = e_cal_dialogs_prompt_retract (GTK_WIDGET (cal_view), comp, &retract_comment, 
&retract);
                if (retract) {
                        icalcomponent *icalcomp;
 
@@ -197,7 +190,7 @@ calendar_view_delete_event (ECalendarView *cal_view,
                        e_cal_ops_send_component (model, event->comp_data->client, icalcomp);
                }
        } else if (e_cal_model_get_confirm_delete (model))
-               delete = delete_component_dialog (
+               delete = e_cal_dialogs_delete_component (
                        comp, FALSE, 1, vtype, GTK_WIDGET (cal_view));
 
        if (delete) {
@@ -208,7 +201,7 @@ calendar_view_delete_event (ECalendarView *cal_view,
 
                if ((itip_organizer_is_user (registry, comp, event->comp_data->client) ||
                     itip_sentby_is_user (registry, comp, event->comp_data->client))
-                   && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
+                   && e_cal_dialogs_cancel_component ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET 
(cal_view)),
                                                event->comp_data->client,
                                                comp, TRUE)) {
                        if (only_occurrence && !e_cal_component_is_instance (comp)) {
@@ -223,7 +216,7 @@ calendar_view_delete_event (ECalendarView *cal_view,
                                e_cal_component_free_datetime (&range.datetime);
                        }
 
-                       itip_send_component (model, E_CAL_COMPONENT_METHOD_CANCEL,
+                       itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_CANCEL,
                                comp, event->comp_data->client, NULL, NULL,
                                NULL, TRUE, FALSE, FALSE);
                }
@@ -639,8 +632,8 @@ calendar_view_component_created_cb (ECalModel *model,
 
        if ((itip_organizer_is_user (registry, comp, client) ||
             itip_sentby_is_user (registry, comp, client)) &&
-            send_component_dialog ((GtkWindow *) toplevel, client, comp, TRUE, &strip_alarms, NULL)) {
-               itip_send_component (model, E_CAL_COMPONENT_METHOD_REQUEST,
+            e_cal_dialogs_send_component ((GtkWindow *) toplevel, client, comp, TRUE, &strip_alarms, NULL)) {
+               itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_REQUEST,
                        comp, client, NULL, NULL, NULL, strip_alarms, FALSE, FALSE);
        }
 
@@ -791,8 +784,8 @@ paste_clipboard_data_free (gpointer ptr)
 
                                if ((itip_organizer_is_user (registry, comp, comp_data->client) ||
                                    itip_sentby_is_user (registry, comp, comp_data->client))
-                                   && cancel_component_dialog ((GtkWindow *) pcd->top_level, 
comp_data->client, comp, TRUE))
-                                       itip_send_component (model, E_CAL_COMPONENT_METHOD_CANCEL,
+                                   && e_cal_dialogs_cancel_component ((GtkWindow *) pcd->top_level, 
comp_data->client, comp, TRUE))
+                                       itip_send_component_with_model (model, E_CAL_COMPONENT_METHOD_CANCEL,
                                                comp, comp_data->client, NULL, NULL, NULL, TRUE, FALSE, TRUE);
 
                                e_cal_component_get_uid (comp, &uid);
@@ -1551,48 +1544,43 @@ e_calendar_view_new_appointment (ECalendarView *cal_view)
 
 /* Ensures the calendar is selected */
 static void
-object_created_cb (CompEditor *ce,
+object_created_cb (ECompEditor *comp_editor,
                    ECalendarView *cal_view)
 {
-       e_cal_model_emit_object_created (e_calendar_view_get_model (cal_view), comp_editor_get_client (ce));
+       e_cal_model_emit_object_created (e_calendar_view_get_model (cal_view), 
e_comp_editor_get_target_client (comp_editor));
 }
 
-CompEditor *
+ECompEditor *
 e_calendar_view_open_event_with_flags (ECalendarView *cal_view,
                                        ECalClient *client,
                                        icalcomponent *icalcomp,
                                        guint32 flags)
 {
-       CompEditor *ce;
-       const gchar *uid;
-       ECalComponent *comp;
+       ECompEditor *comp_editor;
        EShell *shell;
 
        /* FIXME ECalendarView should own an EShell pointer. */
        shell = e_shell_get_default ();
 
-       uid = icalcomponent_get_uid (icalcomp);
+       comp_editor = e_comp_editor_find_existing_for (e_client_get_source (E_CLIENT (client)), icalcomp);
+       if (!comp_editor) {
+               GtkWidget *toplevel;
 
-       ce = comp_editor_find_instance (uid);
-       if (!ce) {
-               ce = event_editor_new (client, shell, flags);
+               toplevel = gtk_widget_get_toplevel (GTK_WIDGET (cal_view));
+               if (!GTK_IS_WINDOW (toplevel))
+                       toplevel = NULL;
+
+               comp_editor = e_comp_editor_open_for_component (GTK_WINDOW (toplevel),
+                       shell, e_client_get_source (E_CLIENT (client)), icalcomp, flags);
 
                g_signal_connect (
-                       ce, "object_created",
+                       comp_editor, "object-created",
                        G_CALLBACK (object_created_cb), cal_view);
-
-               comp = e_cal_component_new ();
-               e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
-               comp_editor_edit_comp (ce, comp);
-               if (flags & COMP_EDITOR_MEETING)
-                       event_editor_show_meeting (EVENT_EDITOR (ce));
-
-               g_object_unref (comp);
        }
 
-       gtk_window_present (GTK_WINDOW (ce));
+       gtk_window_present (GTK_WINDOW (comp_editor));
 
-       return ce;
+       return comp_editor;
 }
 
 /**
@@ -1626,11 +1614,11 @@ e_calendar_view_edit_appointment (ECalendarView *cal_view,
            || mode == EDIT_EVENT_FORCE_MEETING) {
                ECalComponent *comp = e_cal_component_new ();
                e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
-               flags |= COMP_EDITOR_MEETING;
+               flags |= E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
                if (itip_organizer_is_user (registry, comp, client) ||
                    itip_sentby_is_user (registry, comp, client) ||
                    !e_cal_component_has_attendees (comp))
-                       flags |= COMP_EDITOR_USER_ORG;
+                       flags |= E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
                g_object_unref (comp);
        }
 
diff --git a/calendar/gui/e-calendar-view.h b/calendar/gui/e-calendar-view.h
index faa36f2..a8832ca 100644
--- a/calendar/gui/e-calendar-view.h
+++ b/calendar/gui/e-calendar-view.h
@@ -25,8 +25,8 @@
 #include <gtk/gtk.h>
 #include <libecal/libecal.h>
 
-#include "e-cal-model.h"
-#include "dialogs/comp-editor.h"
+#include <calendar/gui/e-cal-model.h>
+#include <calendar/gui/e-comp-editor.h>
 
 /* Standard GObject macros */
 #define E_TYPE_CALENDAR_VIEW \
@@ -216,7 +216,7 @@ void                e_calendar_view_update_query    (ECalendarView *cal_view);
 
 void           e_calendar_view_delete_selected_occurrence
                                                (ECalendarView *cal_view);
-CompEditor *   e_calendar_view_open_event_with_flags
+ECompEditor *  e_calendar_view_open_event_with_flags
                                                (ECalendarView *cal_view,
                                                 ECalClient *client,
                                                 icalcomponent *icalcomp,
diff --git a/calendar/gui/e-comp-editor-event.c b/calendar/gui/e-comp-editor-event.c
new file mode 100644
index 0000000..1089040
--- /dev/null
+++ b/calendar/gui/e-comp-editor-event.c
@@ -0,0 +1,724 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "calendar-config.h"
+#include "comp-util.h"
+#include "e-comp-editor.h"
+#include "e-comp-editor-page.h"
+#include "e-comp-editor-page-attachments.h"
+#include "e-comp-editor-page-general.h"
+#include "e-comp-editor-page-recurrence.h"
+#include "e-comp-editor-page-reminders.h"
+#include "e-comp-editor-page-schedule.h"
+#include "e-comp-editor-property-parts.h"
+#include "e-timezone-entry.h"
+
+#include "e-comp-editor-event.h"
+
+struct _ECompEditorEventPrivate {
+       ECompEditorPage *page_general;
+       ECompEditorPropertyPart *dtstart;
+       ECompEditorPropertyPart *dtend;
+       ECompEditorPropertyPart *categories;
+       ECompEditorPropertyPart *timezone;
+       ECompEditorPropertyPart *transparency;
+       GtkWidget *all_day_check;
+
+       gpointer in_the_past_alert;
+       gpointer insensitive_info_alert;
+};
+
+G_DEFINE_TYPE (ECompEditorEvent, e_comp_editor_event, E_TYPE_COMP_EDITOR)
+
+static void
+ece_event_action_classification_cb (GtkRadioAction *action,
+                                   GtkRadioAction *current,
+                                   ECompEditorEvent *event_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
+
+       e_comp_editor_set_changed (E_COMP_EDITOR (event_editor), TRUE);
+}
+
+static void
+ece_event_update_times (ECompEditorEvent *event_editor,
+                       EDateEdit *date_edit,
+                       gboolean change_end_datetime)
+{
+       GtkWidget *widget;
+       guint flags;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
+       g_return_if_fail (E_IS_DATE_EDIT (date_edit));
+
+       widget = e_date_edit_get_entry (date_edit);
+       if (widget && gtk_widget_has_focus (widget))
+               return;
+
+       if (!e_comp_editor_get_updating (E_COMP_EDITOR (event_editor))) {
+               e_comp_editor_ensure_start_before_end (E_COMP_EDITOR (event_editor),
+                       event_editor->priv->dtstart,
+                       event_editor->priv->dtend,
+                       change_end_datetime);
+       }
+
+       flags = e_comp_editor_get_flags (E_COMP_EDITOR (event_editor));
+
+       if ((flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0) {
+               struct icaltimetype start_tt;
+
+               start_tt = e_comp_editor_property_part_datetime_get_value (
+                       E_COMP_EDITOR_PROPERTY_PART_DATETIME (event_editor->priv->dtstart));
+
+               if (cal_comp_util_compare_time_with_today (start_tt) < 0) {
+                       if (!event_editor->priv->in_the_past_alert) {
+                               EAlert *alert;
+
+                               alert = e_comp_editor_add_warning (E_COMP_EDITOR (event_editor),
+                                       _("Event's time is in the past"), NULL);
+
+                               event_editor->priv->in_the_past_alert = alert;
+
+                               if (alert)
+                                       g_object_add_weak_pointer (G_OBJECT (alert), 
&event_editor->priv->in_the_past_alert);
+
+                               g_clear_object (&alert);
+                       }
+               } else if (event_editor->priv->in_the_past_alert) {
+                       e_alert_response (event_editor->priv->in_the_past_alert, GTK_RESPONSE_OK);
+               }
+       }
+}
+
+static void
+ece_event_dtstart_changed_cb (EDateEdit *date_edit,
+                             ECompEditorEvent *event_editor)
+{
+       g_return_if_fail (E_IS_DATE_EDIT (date_edit));
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
+
+       ece_event_update_times (event_editor, date_edit, TRUE);
+}
+
+static void
+ece_event_dtend_changed_cb (EDateEdit *date_edit,
+                           ECompEditorEvent *event_editor)
+{
+       g_return_if_fail (E_IS_DATE_EDIT (date_edit));
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
+
+       ece_event_update_times (event_editor, date_edit, FALSE);
+}
+
+static void
+ece_editor_all_day_toggled_cb (ECompEditorEvent *event_editor)
+{
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->dtstart);
+
+       ece_event_update_times (event_editor, E_DATE_EDIT (edit_widget), TRUE);
+
+       e_comp_editor_ensure_changed (E_COMP_EDITOR (event_editor));
+}
+
+static void
+ece_event_sensitize_widgets (ECompEditor *comp_editor,
+                            gboolean force_insensitive)
+{
+       ECompEditorEvent *event_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (comp_editor));
+
+       E_COMP_EDITOR_CLASS (e_comp_editor_event_parent_class)->sensitize_widgets (comp_editor, 
force_insensitive);
+
+       event_editor = E_COMP_EDITOR_EVENT (comp_editor);
+       gtk_widget_set_sensitive (event_editor->priv->all_day_check, !force_insensitive);
+
+       if (force_insensitive) {
+               ECalClient *client;
+               const gchar *message = NULL;
+
+               client = e_comp_editor_get_target_client (comp_editor);
+               if (!client)
+                       message = _("Event cannot be edited, because the selected calendar could not be 
opened");
+               else if (e_client_is_readonly (E_CLIENT (client)))
+                       message = _("Event cannot be edited, because the selected calendar is read only");
+
+               if (message) {
+                       EAlert *alert;
+
+                       alert = e_comp_editor_add_information (comp_editor, message, NULL);
+
+                       if (event_editor->priv->insensitive_info_alert)
+                               e_alert_response (event_editor->priv->insensitive_info_alert, 
GTK_RESPONSE_OK);
+
+                       event_editor->priv->insensitive_info_alert = alert;
+
+                       if (alert)
+                               g_object_add_weak_pointer (G_OBJECT (alert), 
&event_editor->priv->insensitive_info_alert);
+
+                       g_clear_object (&alert);
+               } else  if (event_editor->priv->insensitive_info_alert) {
+                       e_alert_response (event_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
+               }
+
+       } else if (event_editor->priv->insensitive_info_alert) {
+               e_alert_response (event_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
+       }
+}
+
+static void
+ece_event_fill_widgets (ECompEditor *comp_editor,
+                       icalcomponent *component)
+{
+       ECompEditorEvent *event_editor;
+       struct icaltimetype dtstart, dtend;
+       icalproperty *prop;
+       icaltimezone *zone = NULL;
+       gboolean all_day_event = FALSE;
+       GtkAction *action;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (comp_editor));
+       g_return_if_fail (component != NULL);
+
+       E_COMP_EDITOR_CLASS (e_comp_editor_event_parent_class)->fill_widgets (comp_editor, component);
+
+       event_editor = E_COMP_EDITOR_EVENT (comp_editor);
+
+       dtstart = icaltime_null_time ();
+       dtend = icaltime_null_time ();
+
+       if (icalcomponent_get_first_property (component, ICAL_DTSTART_PROPERTY)) {
+               dtstart = icalcomponent_get_dtstart (component);
+               if (icaltime_is_valid_time (dtstart))
+                       zone = (icaltimezone *) dtstart.zone;
+       }
+
+       if (icalcomponent_get_first_property (component, ICAL_DTEND_PROPERTY)) {
+               dtend = icalcomponent_get_dtend (component);
+               if (!zone && icaltime_is_valid_time (dtend))
+                       zone = (icaltimezone *) dtend.zone;
+       }
+
+       if (!zone) {
+               struct icaltimetype itt;
+
+               itt = icalcomponent_get_due (component);
+               if (icaltime_is_valid_time (itt))
+                       zone = (icaltimezone *) itt.zone;
+       }
+
+       if (zone) {
+               ECompEditorEvent *event_editor;
+               GtkWidget *edit_widget;
+
+               event_editor = E_COMP_EDITOR_EVENT (comp_editor);
+               edit_widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->timezone);
+
+               e_timezone_entry_set_timezone (E_TIMEZONE_ENTRY (edit_widget), zone);
+
+               if (zone == calendar_config_get_icaltimezone ()) {
+                       /* Hide timezone part */
+                       GtkAction *action;
+
+                       action = e_comp_editor_get_action (comp_editor, "view-timezone");
+                       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+               }
+       }
+
+       if (icaltime_is_valid_time (dtstart) && !icaltime_is_null_time (dtstart) &&
+           (!icaltime_is_valid_time (dtend) || icaltime_is_null_time (dtend))) {
+               dtend = dtstart;
+               if (dtstart.is_date)
+                       icaltime_adjust (&dtend, 1, 0, 0, 0);
+       }
+
+       if (icaltime_is_valid_time (dtend) && !icaltime_is_null_time (dtend)) {
+               ECompEditorEvent *event_editor;
+
+               if (dtstart.is_date && dtend.is_date) {
+                       all_day_event = TRUE;
+                       if (icaltime_compare_date_only (dtend, dtstart) > 0) {
+                               icaltime_adjust (&dtend, -1, 0, 0, 0);
+                       }
+               }
+
+               event_editor = E_COMP_EDITOR_EVENT (comp_editor);
+               e_comp_editor_property_part_datetime_set_value (
+                       E_COMP_EDITOR_PROPERTY_PART_DATETIME (event_editor->priv->dtend), dtend);
+       }
+
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (event_editor->priv->all_day_check), all_day_event);
+
+       prop = icalcomponent_get_first_property (component, ICAL_CLASS_PROPERTY);
+       if (prop && icalproperty_get_class (prop) == ICAL_CLASS_PRIVATE)
+               action = e_comp_editor_get_action (comp_editor, "classify-private");
+       else if (prop && icalproperty_get_class (prop) == ICAL_CLASS_CONFIDENTIAL)
+               action = e_comp_editor_get_action (comp_editor, "classify-confidential");
+       else
+               action = e_comp_editor_get_action (comp_editor, "classify-public");
+
+       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+}
+
+static gboolean
+ece_event_fill_component (ECompEditor *comp_editor,
+                         icalcomponent *component)
+{
+       ECompEditorEvent *event_editor;
+       gboolean date_valid, time_valid;
+       icalproperty *dtstart_prop, *dtend_prop;
+       icalproperty *prop;
+       icalproperty_class class_value;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       if (!E_COMP_EDITOR_CLASS (e_comp_editor_event_parent_class)->fill_component (comp_editor, component))
+               return FALSE;
+
+       event_editor = E_COMP_EDITOR_EVENT (comp_editor);
+
+       if (!e_comp_editor_property_part_datetime_check_validity (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (event_editor->priv->dtstart), &date_valid, 
&time_valid)) {
+               const gchar *error_message = NULL;
+
+               if (!date_valid)
+                       error_message = g_strdup (_("Start date is not a valid date"));
+               else if (!time_valid)
+                       error_message = g_strdup (_("Start time is not a valid time"));
+
+               e_comp_editor_set_validation_error (comp_editor, event_editor->priv->page_general,
+                       e_comp_editor_property_part_get_edit_widget (event_editor->priv->dtstart),
+                       error_message ? error_message : _("Unknown error"));
+
+               return FALSE;
+       }
+
+       if (!e_comp_editor_property_part_datetime_check_validity (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (event_editor->priv->dtend), &date_valid, &time_valid)) {
+               const gchar *error_message = NULL;
+
+               if (!date_valid)
+                       error_message = g_strdup (_("End date is not a valid date"));
+               else if (!time_valid)
+                       error_message = g_strdup (_("End time is not a valid time"));
+
+               e_comp_editor_set_validation_error (comp_editor, event_editor->priv->page_general,
+                       e_comp_editor_property_part_get_edit_widget (event_editor->priv->dtend),
+                       error_message ? error_message : _("Unknown error"));
+
+               return FALSE;
+       }
+
+       dtstart_prop = icalcomponent_get_first_property (component, ICAL_DTSTART_PROPERTY);
+       dtend_prop = icalcomponent_get_first_property (component, ICAL_DTEND_PROPERTY);
+
+       if (dtstart_prop && dtend_prop) {
+               struct icaltimetype dtstart, dtend;
+               gboolean set_dtstart = FALSE, set_dtend = FALSE;
+
+               dtstart = icalproperty_get_dtstart (dtstart_prop);
+               dtend = icalproperty_get_dtend (dtend_prop);
+
+               if (dtstart.is_date && dtend.is_date) {
+                       ECalClient *client;
+
+                       /* Add 1 day to DTEND, as it is not inclusive. */
+                       icaltime_adjust (&dtend, 1, 0, 0, 0);
+                       set_dtend = TRUE;
+
+                       client = e_comp_editor_get_target_client (comp_editor);
+                       if (client && e_client_check_capability (E_CLIENT (client), 
CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME)) {
+                               ECompEditorEvent *event_editor = E_COMP_EDITOR_EVENT (comp_editor);
+                               GtkWidget *timezone_entry;
+
+                               dtstart.is_date = FALSE;
+                               dtstart.hour = 0;
+                               dtstart.minute = 0;
+                               dtstart.second = 0;
+
+                               dtend.is_date = FALSE;
+                               dtend.hour = 0;
+                               dtend.minute = 0;
+                               dtend.second = 0;
+
+                               timezone_entry = e_comp_editor_property_part_get_edit_widget 
(event_editor->priv->timezone);
+
+                               dtstart.zone = e_timezone_entry_get_timezone (E_TIMEZONE_ENTRY 
(timezone_entry));
+                               if (!dtstart.zone)
+                                       dtstart.zone = icaltimezone_get_utc_timezone ();
+                               dtstart.is_utc = dtstart.zone == icaltimezone_get_utc_timezone ();
+
+                               dtend.zone = dtstart.zone;
+                               dtend.is_utc = dtstart.is_utc;
+
+                               set_dtstart = TRUE;
+                               set_dtend = TRUE;
+                       }
+               }
+
+               if (set_dtstart) {
+                       icalproperty_set_dtstart (dtstart_prop, dtstart);
+                       cal_comp_util_update_tzid_parameter (dtstart_prop, dtstart);
+               }
+
+               if (set_dtend) {
+                       icalproperty_set_dtend (dtend_prop, dtend);
+                       cal_comp_util_update_tzid_parameter (dtend_prop, dtend);
+               }
+       }
+
+       if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
+               e_comp_editor_get_action (comp_editor, "classify-private"))))
+               class_value = ICAL_CLASS_PRIVATE;
+       else if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (
+               e_comp_editor_get_action (comp_editor, "classify-confidential"))))
+               class_value = ICAL_CLASS_CONFIDENTIAL;
+       else
+               class_value = ICAL_CLASS_PUBLIC;
+
+       prop = icalcomponent_get_first_property (component, ICAL_CLASS_PROPERTY);
+       if (prop) {
+               icalproperty_set_class (prop, class_value);
+       } else {
+               prop = icalproperty_new_class (class_value);
+               icalcomponent_add_property (component, prop);
+       }
+
+       return TRUE;
+}
+
+static void
+ece_event_setup_ui (ECompEditorEvent *event_editor)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='view-menu'>"
+               "      <placeholder name='parts'>"
+               "        <menuitem action='view-timezone'/>"
+               "        <menuitem action='view-categories'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='toggles'>"
+               "        <menuitem action='all-day-event'/>"
+               "        <menuitem action='show-time-busy'/>"
+               "        <menu action='classification-menu'>"
+               "          <menuitem action='classify-public'/>"
+               "          <menuitem action='classify-private'/>"
+               "          <menuitem action='classify-confidential'/>"
+               "        </menu>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "  <toolbar name='main-toolbar'>"
+               "    <placeholder name='content'>\n"
+               "      <toolitem action='all-day-event'/>\n"
+               "      <toolitem action='show-time-busy'/>\n"
+               "    </placeholder>"
+               "  </toolbar>"
+               "</ui>";
+
+       const GtkToggleActionEntry view_actions[] = {
+               { "view-categories",
+                 NULL,
+                 N_("_Categories"),
+                 NULL,
+                 N_("Toggles whether to display categories"),
+                 NULL,
+                 FALSE },
+
+               { "view-timezone",
+                 "stock_timezone",
+                 N_("Time _Zone"),
+                 NULL,
+                 N_("Toggles whether the time zone is displayed"),
+                 NULL,
+                 FALSE },
+
+               { "all-day-event",
+                 "stock_new-24h-appointment",
+                 N_("All _Day Event"),
+                 NULL,
+                 N_("Toggles whether to have All Day Event"),
+                 NULL,
+                 FALSE },
+
+               { "show-time-busy",
+                 "dialog-error",
+                 N_("Show Time as _Busy"),
+                 NULL,
+                 N_("Toggles whether to show time as busy"),
+                 NULL,
+                 FALSE }
+       };
+
+       const GtkRadioActionEntry classification_radio_entries[] = {
+
+               { "classify-public",
+                 NULL,
+                 N_("Pu_blic"),
+                 NULL,
+                 N_("Classify as public"),
+                 ICAL_CLASS_PUBLIC },
+
+               { "classify-private",
+                 NULL,
+                 N_("_Private"),
+                 NULL,
+                 N_("Classify as private"),
+                 ICAL_CLASS_PRIVATE },
+
+               { "classify-confidential",
+                 NULL,
+                 N_("_Confidential"),
+                 NULL,
+                 N_("Classify as confidential"),
+                 ICAL_CLASS_CONFIDENTIAL }
+       };
+
+       ECompEditor *comp_editor;
+       GSettings *settings;
+       GtkUIManager *ui_manager;
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       GtkWidget *widget;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_EVENT (event_editor));
+
+       comp_editor = E_COMP_EDITOR (event_editor);
+       settings = e_comp_editor_get_settings (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_toggle_actions (action_group,
+               view_actions, G_N_ELEMENTS (view_actions), event_editor);
+
+       gtk_action_group_add_radio_actions (
+               action_group, classification_radio_entries,
+               G_N_ELEMENTS (classification_radio_entries),
+               ICAL_CLASS_PUBLIC,
+               G_CALLBACK (ece_event_action_classification_cb), event_editor);
+
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+       e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.event-editor", event_editor);
+       e_plugin_ui_enable_manager (ui_manager, "org.gnome.evolution.event-editor");
+
+       if (error) {
+               g_critical ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+
+       action = e_comp_editor_get_action (comp_editor, "view-categories");
+       e_binding_bind_property (
+               event_editor->priv->categories, "visible",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_settings_bind (
+               settings, "editor-show-categories",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+
+       action = e_comp_editor_get_action (comp_editor, "view-timezone");
+       e_binding_bind_property (
+               event_editor->priv->timezone, "visible",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_settings_bind (
+               settings, "editor-show-timezone",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+
+       action = e_comp_editor_get_action (comp_editor, "all-day-event");
+       e_binding_bind_property (
+               event_editor->priv->all_day_check, "active",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+       widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->transparency);
+       action = e_comp_editor_get_action (comp_editor, "show-time-busy");
+       e_binding_bind_property (
+               widget, "active",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+}
+
+static void
+e_comp_editor_event_constructed (GObject *object)
+{
+       ECompEditor *comp_editor;
+       ECompEditorEvent *event_editor;
+       ECompEditorPage *page;
+       ECompEditorPropertyPart *part;
+       ECompEditorPropertyPart *summary;
+       EFocusTracker *focus_tracker;
+       GtkWidget *widget;
+
+       G_OBJECT_CLASS (e_comp_editor_event_parent_class)->constructed (object);
+
+       event_editor = E_COMP_EDITOR_EVENT (object);
+       comp_editor = E_COMP_EDITOR (event_editor);
+       focus_tracker = e_comp_editor_get_focus_tracker (comp_editor);
+
+       page = e_comp_editor_page_general_new (comp_editor,
+               _("_Calendar:"), E_SOURCE_EXTENSION_CALENDAR,
+               NULL, FALSE, 2);
+       event_editor->priv->page_general = page;
+
+       part = e_comp_editor_property_part_summary_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 2, 3, 1);
+       summary = part;
+
+       part = e_comp_editor_property_part_location_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 3, 3, 1);
+
+       part = e_comp_editor_property_part_dtstart_new (C_("ECompEditor", "_Start time:"), FALSE, FALSE);
+       e_comp_editor_page_add_property_part (page, part, 0, 4, 2, 1);
+       event_editor->priv->dtstart = part;
+
+       part = e_comp_editor_property_part_dtend_new (C_("ECompEditor", "_End time:"), FALSE, FALSE);
+       e_comp_editor_page_add_property_part (page, part, 0, 5, 2, 1);
+       event_editor->priv->dtend = part;
+
+       part = e_comp_editor_property_part_timezone_new ();
+       e_comp_editor_page_add_property_part (page, part, 0, 6, 3, 1);
+       event_editor->priv->timezone = part;
+
+       widget = gtk_check_button_new_with_mnemonic (C_("ECompEditor", "All da_y event"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_grid_attach (GTK_GRID (page), widget, 2, 4, 1, 1);
+       gtk_widget_show (widget);
+       event_editor->priv->all_day_check = widget;
+
+       part = e_comp_editor_property_part_transparency_new ();
+       e_comp_editor_page_add_property_part (page, part, 2, 5, 1, 1);
+       event_editor->priv->transparency = part;
+
+       widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->transparency);
+       /* Transparency checkbox is not shown in the page, even it's packed there */
+       gtk_widget_hide (widget);
+
+       part = e_comp_editor_property_part_categories_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 7, 3, 1);
+       event_editor->priv->categories = part;
+
+       part = e_comp_editor_property_part_description_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 8, 3, 1);
+
+       widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->timezone);
+       e_comp_editor_property_part_datetime_attach_timezone_entry (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (event_editor->priv->dtstart),
+               E_TIMEZONE_ENTRY (widget));
+       e_comp_editor_property_part_datetime_attach_timezone_entry (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (event_editor->priv->dtend),
+               E_TIMEZONE_ENTRY (widget));
+
+       e_comp_editor_set_time_parts (comp_editor, event_editor->priv->dtstart, event_editor->priv->dtend);
+
+       widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->dtstart);
+       e_binding_bind_property (
+               event_editor->priv->all_day_check, "active",
+               widget, "show-time",
+               G_BINDING_INVERT_BOOLEAN | G_BINDING_BIDIRECTIONAL);
+       g_signal_connect (widget, "changed", G_CALLBACK (ece_event_dtstart_changed_cb), event_editor);
+
+       widget = e_comp_editor_property_part_get_edit_widget (event_editor->priv->dtend);
+       e_binding_bind_property (
+               event_editor->priv->all_day_check, "active",
+               widget, "show-time",
+               G_BINDING_INVERT_BOOLEAN | G_BINDING_BIDIRECTIONAL);
+       g_signal_connect (widget, "changed", G_CALLBACK (ece_event_dtend_changed_cb), event_editor);
+
+       e_signal_connect_notify_swapped (event_editor->priv->all_day_check, "notify::active",
+               G_CALLBACK (ece_editor_all_day_toggled_cb), event_editor);
+
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "General"), page);
+
+       page = e_comp_editor_page_reminders_new (comp_editor);
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "Reminders"), page);
+
+       page = e_comp_editor_page_recurrence_new (comp_editor);
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "Recurrence"), page);
+
+       page = e_comp_editor_page_attachments_new (comp_editor);
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "Attachments"), page);
+
+       page = e_comp_editor_page_schedule_new (comp_editor,
+               e_comp_editor_page_general_get_meeting_store (
+               E_COMP_EDITOR_PAGE_GENERAL (event_editor->priv->page_general)));
+       e_binding_bind_property (
+               event_editor->priv->page_general, "show-attendees",
+               page, "visible",
+               G_BINDING_SYNC_CREATE);
+
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "Schedule"), page);
+
+       ece_event_setup_ui (event_editor);
+
+       widget = e_comp_editor_property_part_get_edit_widget (summary);
+       e_binding_bind_property (widget, "text", comp_editor, "title-suffix", 0);
+       /* Do this as the last thing, because some widgets can call the function as well */
+       gtk_widget_grab_focus (widget);
+}
+
+static void
+e_comp_editor_event_init (ECompEditorEvent *event_editor)
+{
+       event_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (event_editor, E_TYPE_COMP_EDITOR_EVENT, 
ECompEditorEventPrivate);
+}
+
+static void
+e_comp_editor_event_class_init (ECompEditorEventClass *klass)
+{
+       GObjectClass *object_class;
+       ECompEditorClass *comp_editor_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorEventPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_comp_editor_event_constructed;
+
+       comp_editor_class = E_COMP_EDITOR_CLASS (klass);
+       comp_editor_class->help_section = "calendar-usage-add-appointment";
+       comp_editor_class->title_format_with_attendees = _("Meeting - %s");
+       comp_editor_class->title_format_without_attendees = _("Appointment - %s");
+       comp_editor_class->icon_name = "appointment-new";
+       comp_editor_class->sensitize_widgets = ece_event_sensitize_widgets;
+       comp_editor_class->fill_widgets = ece_event_fill_widgets;
+       comp_editor_class->fill_component = ece_event_fill_component;
+}
diff --git a/calendar/gui/e-comp-editor-event.h b/calendar/gui/e-comp-editor-event.h
new file mode 100644
index 0000000..d0ff499
--- /dev/null
+++ b/calendar/gui/e-comp-editor-event.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_EVENT_H
+#define E_COMP_EDITOR_EVENT_H
+
+#include <calendar/gui/e-comp-editor.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_EVENT \
+       (e_comp_editor_event_get_type ())
+#define E_COMP_EDITOR_EVENT(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_EVENT, ECompEditorEvent))
+#define E_COMP_EDITOR_EVENT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_EVENT, ECompEditorEventClass))
+#define E_IS_COMP_EDITOR_EVENT(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_EVENT))
+#define E_IS_COMP_EDITOR_EVENT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_EVENT))
+#define E_COMP_EDITOR_EVENT_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_EVENT, ECompEditorEventClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECompEditorEvent ECompEditorEvent;
+typedef struct _ECompEditorEventClass ECompEditorEventClass;
+typedef struct _ECompEditorEventPrivate ECompEditorEventPrivate;
+
+struct _ECompEditorEvent {
+       ECompEditor parent;
+
+       ECompEditorEventPrivate *priv;
+};
+
+struct _ECompEditorEventClass {
+       ECompEditorClass parent_class;
+};
+
+GType          e_comp_editor_event_get_type    (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_EVENT_H */
diff --git a/calendar/gui/e-comp-editor-memo.c b/calendar/gui/e-comp-editor-memo.c
new file mode 100644
index 0000000..b8112e0
--- /dev/null
+++ b/calendar/gui/e-comp-editor-memo.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "e-comp-editor.h"
+#include "e-comp-editor-page-attachments.h"
+#include "e-comp-editor-page-general.h"
+#include "e-comp-editor-property-part.h"
+#include "e-comp-editor-property-parts.h"
+
+#include "e-comp-editor-memo.h"
+
+struct _ECompEditorMemoPrivate {
+       ECompEditorPropertyPart *categories;
+};
+
+G_DEFINE_TYPE (ECompEditorMemo, e_comp_editor_memo, E_TYPE_COMP_EDITOR)
+
+static void
+ece_memo_setup_ui (ECompEditorMemo *memo_editor)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='view-menu'>"
+               "      <placeholder name='parts'>"
+               "        <menuitem action='view-categories'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "</ui>";
+
+       const GtkToggleActionEntry view_actions[] = {
+               { "view-categories",
+                 NULL,
+                 N_("_Categories"),
+                 NULL,
+                 N_("Toggles whether to display categories"),
+                 NULL,
+                 FALSE }
+       };
+
+       ECompEditor *comp_editor;
+       GSettings *settings;
+       GtkUIManager *ui_manager;
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_MEMO (memo_editor));
+
+       comp_editor = E_COMP_EDITOR (memo_editor);
+       settings = e_comp_editor_get_settings (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_toggle_actions (action_group,
+               view_actions, G_N_ELEMENTS (view_actions), memo_editor);
+
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+       e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.memo-editor", memo_editor);
+       e_plugin_ui_enable_manager (ui_manager, "org.gnome.evolution.memo-editor");
+
+       if (error) {
+               g_critical ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+
+       action = e_comp_editor_get_action (comp_editor, "view-categories");
+       e_binding_bind_property (
+               memo_editor->priv->categories, "visible",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_settings_bind (
+               settings, "editor-show-categories",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+e_comp_editor_memo_constructed (GObject *object)
+{
+       ECompEditorMemo *memo_editor;
+       ECompEditor *comp_editor;
+       ECompEditorPage *page;
+       ECompEditorPropertyPart *part, *summary;
+       EFocusTracker *focus_tracker;
+       GtkWidget *edit_widget;
+
+       G_OBJECT_CLASS (e_comp_editor_memo_parent_class)->constructed (object);
+
+       memo_editor = E_COMP_EDITOR_MEMO (object);
+       comp_editor = E_COMP_EDITOR (memo_editor);
+       focus_tracker = e_comp_editor_get_focus_tracker (comp_editor);
+
+       page = e_comp_editor_page_general_new (comp_editor,
+               _("_List:"), E_SOURCE_EXTENSION_MEMO_LIST,
+               NULL, FALSE, 1);
+
+       part = e_comp_editor_property_part_summary_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 2, 2, 1);
+       summary = part;
+
+       part = e_comp_editor_property_part_dtstart_new (C_("ECompEditor", "Sta_rt date:"), TRUE, TRUE);
+       e_comp_editor_page_add_property_part (page, part, 0, 3, 2, 1);
+
+       part = e_comp_editor_property_part_classification_new ();
+       e_comp_editor_page_add_property_part (page, part, 0, 4, 2, 1);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       gtk_widget_set_halign (edit_widget, GTK_ALIGN_START);
+       gtk_widget_set_hexpand (edit_widget, FALSE);
+
+       part = e_comp_editor_property_part_categories_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 5, 2, 1);
+       memo_editor->priv->categories = part;
+
+       part = e_comp_editor_property_part_description_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 6, 2, 1);
+
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "General"), page);
+
+       page = e_comp_editor_page_attachments_new (comp_editor);
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "Attachments"), page);
+
+       ece_memo_setup_ui (memo_editor);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (summary);
+       e_binding_bind_property (edit_widget, "text", comp_editor, "title-suffix", 0);
+       gtk_widget_grab_focus (edit_widget);
+}
+
+static void
+e_comp_editor_memo_init (ECompEditorMemo *memo_editor)
+{
+       memo_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (memo_editor, E_TYPE_COMP_EDITOR_MEMO, 
ECompEditorMemoPrivate);
+}
+
+static void
+e_comp_editor_memo_class_init (ECompEditorMemoClass *klass)
+{
+       GObjectClass *object_class;
+       ECompEditorClass *comp_editor_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorMemoPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_comp_editor_memo_constructed;
+
+       comp_editor_class = E_COMP_EDITOR_CLASS (klass);
+       comp_editor_class->help_section = "memos-usage";
+       comp_editor_class->title_format_with_attendees = _("Assigned Memo - %s");
+       comp_editor_class->title_format_without_attendees = _("Memo - %s");
+       comp_editor_class->icon_name = "stock_insert-note";
+}
diff --git a/calendar/gui/e-comp-editor-memo.h b/calendar/gui/e-comp-editor-memo.h
new file mode 100644
index 0000000..0b777ce
--- /dev/null
+++ b/calendar/gui/e-comp-editor-memo.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_MEMO_H
+#define E_COMP_EDITOR_MEMO_H
+
+#include <calendar/gui/e-comp-editor.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_MEMO \
+       (e_comp_editor_memo_get_type ())
+#define E_COMP_EDITOR_MEMO(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_MEMO, ECompEditorMemo))
+#define E_COMP_EDITOR_MEMO_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_MEMO, ECompEditorMemoClass))
+#define E_IS_COMP_EDITOR_MEMO(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_MEMO))
+#define E_IS_COMP_EDITOR_MEMO_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_MEMO))
+#define E_COMP_EDITOR_MEMO_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_MEMO, ECompEditorMemoClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECompEditorMemo ECompEditorMemo;
+typedef struct _ECompEditorMemoClass ECompEditorMemoClass;
+typedef struct _ECompEditorMemoPrivate ECompEditorMemoPrivate;
+
+struct _ECompEditorMemo {
+       ECompEditor parent;
+
+       ECompEditorMemoPrivate *priv;
+};
+
+struct _ECompEditorMemoClass {
+       ECompEditorClass parent_class;
+};
+
+GType          e_comp_editor_memo_get_type     (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_MEMO_H */
diff --git a/calendar/gui/e-comp-editor-page-attachments.c b/calendar/gui/e-comp-editor-page-attachments.c
new file mode 100644
index 0000000..085e13c
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-attachments.c
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "comp-util.h"
+
+#include "e-comp-editor-page-attachments.h"
+
+#define NUM_VIEWS 2
+
+struct _ECompEditorPageAttachmentsPrivate {
+       GtkTreeModel *store;
+       GtkWidget *notebook;
+       GtkWidget *combo_box;
+       GtkWidget *controls_container;
+       GtkWidget *icon_view;
+       GtkWidget *tree_view;
+       GtkWidget *status_icon;
+       GtkWidget *status_label;
+
+       gulong store_row_inserted_handler_id;
+       gulong store_row_deleted_handler_id;
+
+       gint active_view;
+       GSList *temporary_files;
+};
+
+enum {
+       PROP_0,
+       PROP_ACTIVE_VIEW
+};
+
+G_DEFINE_TYPE (ECompEditorPageAttachments, e_comp_editor_page_attachments, E_TYPE_COMP_EDITOR_PAGE)
+
+static void
+ecep_attachments_action_attach_cb (GtkAction *action,
+                                  ECompEditorPageAttachments *page_attachments)
+{
+       ECompEditor *comp_editor;
+       EAttachmentStore *store;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_attachments));
+       store = E_ATTACHMENT_STORE (page_attachments->priv->store);
+
+       e_attachment_store_run_load_dialog (store, GTK_WINDOW (comp_editor));
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_attachments_select_page_cb (GtkAction *action,
+                                ECompEditorPage *page)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page));
+
+       e_comp_editor_page_select (page);
+}
+
+static void
+temporary_file_free (gpointer ptr)
+{
+       gchar *temporary_file = ptr;
+
+       if (temporary_file) {
+               gchar *sep;
+
+               g_unlink (temporary_file);
+
+               sep = strrchr (temporary_file, G_DIR_SEPARATOR);
+               if (sep) {
+                       *sep = '\0';
+                       g_rmdir (temporary_file);
+               }
+
+               g_free (temporary_file);
+       }
+}
+
+static void
+ecep_attachments_update_status (ECompEditorPageAttachments *page_attachments)
+{
+       EAttachmentStore *store;
+       GtkLabel *label;
+       guint num_attachments;
+       guint64 total_size;
+       gchar *display_size;
+       gchar *markup;
+
+       store = E_ATTACHMENT_STORE (page_attachments->priv->store);
+       label = GTK_LABEL (page_attachments->priv->status_label);
+
+       num_attachments = e_attachment_store_get_num_attachments (store);
+       total_size = e_attachment_store_get_total_size (store);
+       display_size = g_format_size (total_size);
+
+       if (total_size > 0)
+               markup = g_strdup_printf (
+                       "<b>%d</b> %s (%s)", num_attachments, g_dngettext (GETTEXT_PACKAGE,
+                       "Attachment", "Attachments", num_attachments),
+                       display_size);
+       else
+               markup = g_strdup_printf (
+                       "<b>%d</b> %s", num_attachments, g_dngettext (GETTEXT_PACKAGE,
+                       "Attachment", "Attachments", num_attachments));
+       gtk_label_set_markup (label, markup);
+       g_free (markup);
+
+       g_free (display_size);
+}
+
+static void
+ecep_attachments_attachment_loaded_cb (EAttachment *attachment,
+                                      GAsyncResult *result,
+                                      ECompEditorPageAttachments *page_attachments)
+{
+       GFileInfo *file_info;
+       const gchar *display_name;
+       const gchar *uid;
+       gchar *new_name;
+       GError *error = NULL;
+
+       /* Prior to 2.27.2, attachment files were named:
+        *
+        *     <component-uid> '-' <actual-filename>
+        *     -------------------------------------
+        *              (one long filename)
+        *
+        * Here we fix the display name if this form is detected so we
+        * don't show the component UID in the user interface.  If the
+        * user saves changes in the editor, the attachment will be
+        * written to disk as:
+        *
+        *     <component-uid> / <actual-filename>
+        *     ---------------   -----------------
+        *       (directory)      (original name)
+        *
+        * So this is a lazy migration from the old form to the new.
+        */
+
+       file_info = e_attachment_ref_file_info (attachment);
+       if (file_info) {
+               display_name = g_file_info_get_display_name (file_info);
+               uid = g_object_get_data (G_OBJECT (attachment), "uid");
+
+               if (g_str_has_prefix (display_name, uid)) {
+                       new_name = g_strdup (display_name + strlen (uid) + 1);
+                       g_file_info_set_display_name (file_info, new_name);
+                       g_object_notify (G_OBJECT (attachment), "file-info");
+                       g_free (new_name);
+               }
+       }
+
+       if (!e_attachment_load_finish (attachment, result, &error)) {
+               GtkTreeRowReference *reference;
+
+               reference = e_attachment_get_reference (attachment);
+               if (gtk_tree_row_reference_valid (reference)) {
+                       GtkTreeModel *model;
+
+                       model = gtk_tree_row_reference_get_model (reference);
+
+                       e_attachment_store_remove_attachment (
+                               E_ATTACHMENT_STORE (model), attachment);
+               }
+
+               /* Ignore cancellations. */
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                       ECompEditor *comp_editor;
+                       EAlert *alert;
+                       gchar *primary_text;
+
+                       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_attachments));
+
+                       if (file_info)
+                               display_name = g_file_info_get_display_name (file_info);
+                       else
+                               display_name = NULL;
+
+                       if (display_name != NULL)
+                               primary_text = g_strdup_printf (_("Could not load '%s'"), display_name);
+                       else
+                               primary_text = g_strdup (_("Could not load the attachment"));
+
+                       alert = e_comp_editor_add_error (comp_editor, primary_text,
+                               error ? error->message : _("Unknown error"));
+
+                       g_clear_object (&comp_editor);
+                       g_clear_object (&alert);
+                       g_free (primary_text);
+               }
+       }
+
+       g_clear_object (&file_info);
+       g_clear_error (&error);
+}
+
+static void
+ecep_attachments_sensitize_widgets (ECompEditorPage *page,
+                                   gboolean force_insensitive)
+{
+       ECompEditorPageAttachments *page_attachments;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page));
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_attachments_parent_class)->sensitize_widgets (page, 
force_insensitive);
+
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (page);
+
+       gtk_widget_set_sensitive (page_attachments->priv->controls_container, !force_insensitive);
+       gtk_widget_set_sensitive (page_attachments->priv->notebook, !force_insensitive);
+}
+
+static void
+ecep_attachments_fill_widgets (ECompEditorPage *page,
+                              icalcomponent *component)
+{
+       ECompEditorPageAttachments *page_attachments;
+       EAttachmentStore *store;
+       icalproperty *prop;
+       const gchar *uid;
+       gint index;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page));
+       g_return_if_fail (component != NULL);
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_attachments_parent_class)->fill_widgets (page, 
component);
+
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (page);
+       store = E_ATTACHMENT_STORE (page_attachments->priv->store);
+
+       uid = icalcomponent_get_uid (component);
+
+       g_slist_free_full (page_attachments->priv->temporary_files, temporary_file_free);
+       page_attachments->priv->temporary_files = NULL;
+
+       e_attachment_store_remove_all (store);
+
+       for (prop = icalcomponent_get_first_property (component, ICAL_ATTACH_PROPERTY), index = 0;
+            prop;
+            prop = icalcomponent_get_next_property (component, ICAL_ATTACH_PROPERTY), index++) {
+               icalattach *attach;
+               gchar *uri = NULL;
+
+               attach = icalproperty_get_attach (prop);
+               if (!attach)
+                       continue;
+
+               if (icalattach_get_is_url (attach)) {
+                       const gchar *data;
+                       gsize buf_size;
+
+                       data = icalattach_get_url (attach);
+                       buf_size = strlen (data);
+                       uri = g_malloc0 (buf_size + 1);
+
+                       icalvalue_decode_ical_string (data, uri, buf_size);
+               } else {
+                       gchar *temporary_filename = NULL;
+                       icalparameter *encoding_par = icalproperty_get_first_parameter (prop, 
ICAL_ENCODING_PARAMETER);
+                       if (encoding_par) {
+                               gchar *str_value = icalproperty_get_value_as_string_r (prop);
+
+                               if (str_value) {
+                                       icalparameter_encoding encoding = icalparameter_get_encoding 
(encoding_par);
+                                       guint8 *data = NULL;
+                                       gsize data_len = 0;
+
+                                       switch (encoding) {
+                                       case ICAL_ENCODING_8BIT:
+                                               data = (guint8 *) str_value;
+                                               data_len = strlen (str_value);
+                                               str_value = NULL;
+                                               break;
+                                       case ICAL_ENCODING_BASE64:
+                                               data = g_base64_decode (str_value, &data_len);
+                                               break;
+                                       default:
+                                               break;
+                                       }
+
+                                       if (data) {
+                                               gchar *dir, *id_str;
+                                               struct icaltimetype rid_tt;
+                                               gchar *rid;
+
+                                               rid_tt = icalcomponent_get_recurrenceid (component);
+                                               if (icaltime_is_null_time (rid_tt) || !icaltime_is_valid_time 
(rid_tt))
+                                                       rid = NULL;
+                                               else
+                                                       rid = icaltime_as_ical_string_r (rid_tt);
+
+                                               id_str = g_strconcat (uid, rid ? "-" : NULL, rid, NULL);
+
+                                               dir = g_build_filename (e_get_user_cache_dir (), "tmp", 
"calendar", id_str, NULL);
+
+                                               g_free (rid);
+                                               g_free (id_str);
+
+                                               if (g_mkdir_with_parents (dir, 0700) >= 0) {
+                                                       icalparameter *param;
+                                                       gchar *file = NULL;
+
+                                                       for (param = icalproperty_get_first_parameter (prop, 
ICAL_X_PARAMETER);
+                                                            param && !file;
+                                                            param = icalproperty_get_next_parameter (prop, 
ICAL_X_PARAMETER)) {
+                                                               if (e_util_strstrcase 
(icalparameter_get_xname (param), "NAME") &&
+                                                                   icalparameter_get_xvalue (param) &&
+                                                                   *icalparameter_get_xvalue (param))
+                                                                       file = g_strdup 
(icalparameter_get_xvalue (param));
+                                                       }
+
+                                                       if (!file)
+                                                               file = g_strdup_printf ("%d.dat", index);
+
+                                                       temporary_filename = g_build_filename (dir, file, 
NULL);
+                                                       if (!g_file_set_contents (temporary_filename, (const 
gchar *) data, data_len, NULL)) {
+                                                               g_free (temporary_filename);
+                                                               temporary_filename = NULL;
+                                                       }
+                                               }
+
+                                               g_free (dir);
+                                       }
+
+                                       g_free (str_value);
+                                       g_free (data);
+                               }
+                       }
+
+                       if (temporary_filename) {
+                               uri = g_filename_to_uri (temporary_filename, NULL, NULL);
+                               page_attachments->priv->temporary_files = g_slist_prepend 
(page_attachments->priv->temporary_files, temporary_filename);
+                       }
+               }
+
+               if (uri) {
+                       EAttachment *attachment;
+
+                       attachment = e_attachment_new_for_uri (uri);
+                       e_attachment_store_add_attachment (store, attachment);
+                       g_object_set_data_full (
+                               G_OBJECT (attachment),
+                               "uid", g_strdup (uid),
+                               (GDestroyNotify) g_free);
+                       e_attachment_load_async (
+                               attachment, (GAsyncReadyCallback)
+                               ecep_attachments_attachment_loaded_cb, page_attachments);
+                       g_object_unref (attachment);
+               }
+
+               g_free (uri);
+       }
+}
+
+static gboolean
+ecep_attachments_fill_component (ECompEditorPage *page,
+                                icalcomponent *component)
+{
+       ECompEditorPageAttachments *page_attachments;
+       ECompEditor *comp_editor;
+       GList *attachments, *link;
+       icalproperty *prop;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       comp_editor = e_comp_editor_page_ref_editor (page);
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (page);
+
+       if (e_attachment_store_get_num_loading (E_ATTACHMENT_STORE (page_attachments->priv->store)) > 0) {
+               e_comp_editor_set_validation_error (comp_editor, page, NULL,
+                       _("Some attachments are still being downloaded. Please wait until the download is 
finished."));
+               g_clear_object (&comp_editor);
+               return FALSE;
+       }
+
+       cal_comp_util_remove_all_properties (component, ICAL_ATTACH_PROPERTY);
+
+       attachments = e_attachment_store_get_attachments (E_ATTACHMENT_STORE (page_attachments->priv->store));
+       for (link = attachments; link; link = g_list_next (link)) {
+               EAttachment *attachment = link->data;
+               icalattach *attach;
+               gsize buf_size;
+               gchar *buf, *uri, *description;
+               GFile *file;
+
+               if (!attachment)
+                       continue;
+
+               description = e_attachment_dup_description (attachment);
+
+               file = e_attachment_ref_file (attachment);
+               if (!file) {
+                       gchar *error_message;
+
+                       success = FALSE;
+
+                       error_message = g_strdup_printf (
+                               _("Attachment '%s' cannot be found, remove it from the list, please"),
+                               description);
+
+                       e_comp_editor_set_validation_error (comp_editor, page, NULL, error_message);
+
+                       g_free (description);
+                       g_free (error_message);
+                       break;
+               }
+
+               uri = g_file_get_uri (file);
+               if (!uri) {
+                       gchar *error_message;
+
+                       success = FALSE;
+
+                       error_message = g_strdup_printf (
+                               _("Attachment '%s' doesn't have valid URI, remove it from the list, please"),
+                               description);
+
+                       e_comp_editor_set_validation_error (comp_editor, page, NULL, error_message);
+
+                       g_free (description);
+                       g_free (error_message);
+                       g_object_unref (file);
+                       break;
+               }
+
+               g_object_unref (file);
+               g_free (description);
+
+               buf_size = 2 * strlen (uri) + 1;
+               buf = g_malloc0 (buf_size);
+
+               icalvalue_encode_ical_string (uri, buf, buf_size);
+               attach = icalattach_new_from_url (buf);
+               prop = icalproperty_new_attach (attach);
+               icalcomponent_add_property (component, prop);
+
+               g_free (buf);
+               g_free (uri);
+       }
+
+       g_list_free_full (attachments, g_object_unref);
+       g_clear_object (&comp_editor);
+
+       return success && E_COMP_EDITOR_PAGE_CLASS 
(e_comp_editor_page_attachments_parent_class)->fill_component (page, component);
+}
+
+static gboolean
+ecep_attachments_drag_motion (GtkWidget *widget,
+                             GdkDragContext *context,
+                             gint x,
+                             gint y,
+                             guint time)
+{
+       ECompEditorPageAttachments *page_attachments;
+       EAttachmentView *attachment_view;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (widget), FALSE);
+
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (widget);
+       attachment_view = E_ATTACHMENT_VIEW (page_attachments->priv->icon_view);
+
+       return e_attachment_view_drag_motion (attachment_view, context, x, y, time);
+}
+
+static void
+ecep_attachments_drag_data_received (GtkWidget *widget,
+                                    GdkDragContext *context,
+                                    gint x,
+                                    gint y,
+                                    GtkSelectionData *selection,
+                                    guint info,
+                                    guint time)
+{
+       ECompEditorPageAttachments *page_attachments;
+       EAttachmentView *attachment_view;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (widget));
+
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (widget);
+       attachment_view = E_ATTACHMENT_VIEW (page_attachments->priv->icon_view);
+
+       /* Forward the data to the attachment view.  Note that calling
+        * e_attachment_view_drag_data_received() will not work because
+        * that function only handles the case where all the other drag
+        * handlers have failed. */
+
+       /* XXX Dirty hack for forwarding drop events. */
+       g_signal_emit_by_name (
+               attachment_view, "drag-data-received",
+               context, x, y, selection, info, time);
+}
+
+static void
+ecep_attachments_set_property (GObject *object,
+                              guint property_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ACTIVE_VIEW:
+                       e_comp_editor_page_attachments_set_active_view (
+                               E_COMP_EDITOR_PAGE_ATTACHMENTS (object),
+                               g_value_get_int (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecep_attachments_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ACTIVE_VIEW:
+                       g_value_set_int (
+                               value,
+                               e_comp_editor_page_attachments_get_active_view (
+                               E_COMP_EDITOR_PAGE_ATTACHMENTS (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecep_attachments_dispose (GObject *object)
+{
+       ECompEditorPageAttachments *page_attachments;
+
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (object);
+
+       if (page_attachments->priv->store) {
+               gpointer store = page_attachments->priv->store;
+
+               e_signal_disconnect_notify_handler (store, 
&page_attachments->priv->store_row_inserted_handler_id);
+               e_signal_disconnect_notify_handler (store, 
&page_attachments->priv->store_row_deleted_handler_id);
+       }
+
+       g_clear_object (&page_attachments->priv->store);
+
+       g_slist_free_full (page_attachments->priv->temporary_files, temporary_file_free);
+       page_attachments->priv->temporary_files = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_page_attachments_parent_class)->dispose (object);
+}
+
+static void
+ecep_attachments_setup_ui (ECompEditorPageAttachments *page_attachments)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='insert-menu'>"
+               "      <menuitem action='attachments-attach'/>"
+               "    </menu>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='tabs'>"
+               "        <menuitem action='page-attachments'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "  <toolbar name='main-toolbar'>"
+               "    <placeholder name='content'>\n"
+               "      <toolitem action='page-attachments'/>\n"
+               "    </placeholder>"
+               "  </toolbar>"
+               "</ui>";
+
+       GtkActionEntry editable_entries[] = {
+               { "attachments-attach",
+                 "mail-attachment",
+                 N_("_Attachment..."),
+                 "<Control>m",
+                 N_("Attach a file"),
+                 G_CALLBACK (ecep_attachments_action_attach_cb) }
+       };
+
+       GtkActionEntry options_entries[] = {
+               { "page-attachments",
+                 "mail-attachment",
+                 N_("_Attachments"),
+                 NULL,
+                 N_("Show attachments"),
+                 G_CALLBACK (ecep_attachments_select_page_cb) }
+       };
+
+       ECompEditor *comp_editor;
+       GtkUIManager *ui_manager;
+       GtkActionGroup *action_group;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_attachments));
+       ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+       action_group = e_comp_editor_get_action_group (comp_editor, "editable");
+
+       gtk_action_group_add_actions (
+               action_group, editable_entries,
+               G_N_ELEMENTS (editable_entries), page_attachments);
+
+       action_group = e_comp_editor_get_action_group (comp_editor, "individual");
+
+       gtk_action_group_add_actions (
+               action_group, options_entries,
+               G_N_ELEMENTS (options_entries), page_attachments);
+
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+       if (error != NULL) {
+               g_warning ("%s: Failed to add UI from string: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_attachments_constructed (GObject *object)
+{
+       ECompEditorPageAttachments *page_attachments;
+       GSettings *settings;
+       GtkSizeGroup *size_group;
+       GtkWidget *container;
+       GtkWidget *widget;
+       GtkAction *action;
+
+       G_OBJECT_CLASS (e_comp_editor_page_attachments_parent_class)->constructed (object);
+
+       page_attachments = E_COMP_EDITOR_PAGE_ATTACHMENTS (object);
+
+       page_attachments->priv->store = e_attachment_store_new ();
+
+       page_attachments->priv->store_row_inserted_handler_id =
+               g_signal_connect_swapped (page_attachments->priv->store, "row-inserted",
+                       G_CALLBACK (e_comp_editor_page_emit_changed), page_attachments);
+       page_attachments->priv->store_row_deleted_handler_id =
+               g_signal_connect_swapped (page_attachments->priv->store, "row-deleted",
+                       G_CALLBACK (e_comp_editor_page_emit_changed), page_attachments);
+
+       /* Keep the expander label and combo box the same height. */
+       size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+       /* Construct the Attachment Views */
+
+       widget = gtk_notebook_new ();
+       gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+       gtk_grid_attach (GTK_GRID (page_attachments), widget, 0, 1, 1, 1);
+       page_attachments->priv->notebook = widget;
+       gtk_widget_show (widget);
+
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+
+       container = page_attachments->priv->notebook;
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget),
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+       gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       widget = e_attachment_icon_view_new ();
+       gtk_widget_set_can_focus (widget, TRUE);
+       gtk_icon_view_set_model (GTK_ICON_VIEW (widget), page_attachments->priv->store);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       page_attachments->priv->icon_view = widget;
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+
+       container = page_attachments->priv->notebook;
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget),
+               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+       gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       widget = e_attachment_tree_view_new ();
+       gtk_widget_set_can_focus (widget, TRUE);
+       gtk_tree_view_set_model (GTK_TREE_VIEW (widget), page_attachments->priv->store);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       page_attachments->priv->tree_view = widget;
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+
+       /* Construct the Controls */
+
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+       gtk_grid_attach (GTK_GRID (page_attachments), widget, 0, 0, 1, 1);
+       gtk_widget_show (widget);
+       page_attachments->priv->controls_container = widget;
+
+       container = widget;
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+       gtk_widget_set_margin_right (widget, 6);
+       gtk_widget_set_margin_left (widget, 6);
+       gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       /* The "Add Attachment" button proxies the "add" action from
+        * one of the two attachment views.  Doesn't matter which. */
+       widget = gtk_button_new ();
+       action = e_attachment_view_get_action (E_ATTACHMENT_VIEW (page_attachments->priv->icon_view), "add");
+       gtk_button_set_image (GTK_BUTTON (widget), gtk_image_new ());
+       gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
+       gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       widget = gtk_combo_box_text_new ();
+       gtk_size_group_add_widget (size_group, widget);
+       gtk_combo_box_text_append_text (
+               GTK_COMBO_BOX_TEXT (widget), _("Icon View"));
+       gtk_combo_box_text_append_text (
+               GTK_COMBO_BOX_TEXT (widget), _("List View"));
+       gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_attachments->priv->combo_box = widget;
+       gtk_widget_show (widget);
+
+       widget = gtk_image_new_from_icon_name (
+               "mail-attachment", GTK_ICON_SIZE_MENU);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_attachments->priv->status_icon = widget;
+       gtk_widget_show (widget);
+
+       widget = gtk_label_new (NULL);
+       gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_attachments->priv->status_label = widget;
+       gtk_widget_show (widget);
+
+       e_signal_connect_notify_swapped (
+               page_attachments->priv->store, "notify::num-attachments",
+               G_CALLBACK (ecep_attachments_update_status), page_attachments);
+
+       e_signal_connect_notify_swapped (
+               page_attachments->priv->store, "notify::total-size",
+               G_CALLBACK (ecep_attachments_update_status), page_attachments);
+
+       g_object_unref (size_group);
+
+       ecep_attachments_update_status (page_attachments);
+
+       e_binding_bind_property (
+               object, "active-view",
+               page_attachments->priv->combo_box, "active",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_property (
+               object, "active-view",
+               page_attachments->priv->notebook, "page",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.shell");
+
+       g_settings_bind (
+               settings, "attachment-view",
+               object, "active-view",
+               G_SETTINGS_BIND_DEFAULT);
+
+       g_clear_object (&settings);
+
+       ecep_attachments_setup_ui (page_attachments);
+}
+
+static void
+e_comp_editor_page_attachments_init (ECompEditorPageAttachments *page_attachments)
+{
+       page_attachments->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_attachments,
+               E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS,
+               ECompEditorPageAttachmentsPrivate);
+}
+
+static void
+e_comp_editor_page_attachments_class_init (ECompEditorPageAttachmentsClass *klass)
+{
+       ECompEditorPageClass *page_class;
+       GtkWidgetClass *widget_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPageAttachmentsPrivate));
+
+       page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
+       page_class->sensitize_widgets = ecep_attachments_sensitize_widgets;
+       page_class->fill_widgets = ecep_attachments_fill_widgets;
+       page_class->fill_component = ecep_attachments_fill_component;
+
+       widget_class = GTK_WIDGET_CLASS (klass);
+       widget_class->drag_motion = ecep_attachments_drag_motion;
+       widget_class->drag_data_received = ecep_attachments_drag_data_received;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = ecep_attachments_set_property;
+       object_class->get_property = ecep_attachments_get_property;
+       object_class->dispose = ecep_attachments_dispose;
+       object_class->constructed = ecep_attachments_constructed;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ACTIVE_VIEW,
+               g_param_spec_int (
+                       "active-view",
+                       "Active View",
+                       NULL,
+                       0,
+                       NUM_VIEWS,
+                       0,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+ECompEditorPage *
+e_comp_editor_page_attachments_new (ECompEditor *editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
+
+       return g_object_new (E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS,
+               "editor", editor,
+               NULL);
+}
+
+gint
+e_comp_editor_page_attachments_get_active_view (ECompEditorPageAttachments *page_attachments)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments), 0);
+
+       return page_attachments->priv->active_view;
+}
+
+void
+e_comp_editor_page_attachments_set_active_view (ECompEditorPageAttachments *page_attachments,
+                                               gint view)
+{
+       EAttachmentView *source;
+       EAttachmentView *target;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
+       g_return_if_fail (view >= 0 && view < NUM_VIEWS);
+
+       if (view == page_attachments->priv->active_view)
+               return;
+
+       page_attachments->priv->active_view = view;
+
+       /* Synchronize the item selection of the view we're
+        * switching TO with the view we're switching FROM. */
+       if (view == 0) {
+               /* from tree view to icon view */
+               source = E_ATTACHMENT_VIEW (page_attachments->priv->tree_view);
+               target = E_ATTACHMENT_VIEW (page_attachments->priv->icon_view);
+       } else {
+               /* from icon view to tree view */
+               source = E_ATTACHMENT_VIEW (page_attachments->priv->icon_view);
+               target = E_ATTACHMENT_VIEW (page_attachments->priv->tree_view);
+       }
+
+       e_attachment_view_sync_selection (source, target);
+
+       g_object_notify (G_OBJECT (page_attachments), "active-view");
+}
+
+EAttachmentStore *
+e_comp_editor_page_attachments_get_store (ECompEditorPageAttachments *page_attachments)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments), NULL);
+
+       return E_ATTACHMENT_STORE (page_attachments->priv->store);
+}
diff --git a/calendar/gui/e-comp-editor-page-attachments.h b/calendar/gui/e-comp-editor-page-attachments.h
new file mode 100644
index 0000000..dfbadb3
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-attachments.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PAGE_ATTACHMENTS_H
+#define E_COMP_EDITOR_PAGE_ATTACHMENTS_H
+
+#include <e-util/e-util.h>
+#include <calendar/gui/e-comp-editor.h>
+#include <calendar/gui/e-comp-editor-page.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS \
+       (e_comp_editor_page_attachments_get_type ())
+#define E_COMP_EDITOR_PAGE_ATTACHMENTS(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS, ECompEditorPageAttachments))
+#define E_COMP_EDITOR_PAGE_ATTACHMENTS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS, ECompEditorPageAttachmentsClass))
+#define E_IS_COMP_EDITOR_PAGE_ATTACHMENTS(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS))
+#define E_IS_COMP_EDITOR_PAGE_ATTACHMENTS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS))
+#define E_COMP_EDITOR_PAGE_ATTACHMENTS_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS, ECompEditorPageAttachmentsClass))
+
+typedef struct _ECompEditorPageAttachments ECompEditorPageAttachments;
+typedef struct _ECompEditorPageAttachmentsClass ECompEditorPageAttachmentsClass;
+typedef struct _ECompEditorPageAttachmentsPrivate ECompEditorPageAttachmentsPrivate;
+
+struct _ECompEditorPageAttachments {
+       ECompEditorPage parent;
+
+       ECompEditorPageAttachmentsPrivate *priv;
+};
+
+struct _ECompEditorPageAttachmentsClass {
+       ECompEditorPageClass parent_class;
+};
+
+GType          e_comp_editor_page_attachments_get_type (void) G_GNUC_CONST;
+ECompEditorPage *
+               e_comp_editor_page_attachments_new      (ECompEditor *editor);
+gint           e_comp_editor_page_attachments_get_active_view
+                                                       (ECompEditorPageAttachments *page_attachments);
+void           e_comp_editor_page_attachments_set_active_view
+                                                       (ECompEditorPageAttachments *page_attachments,
+                                                        gint view);
+EAttachmentStore *
+               e_comp_editor_page_attachments_get_store
+                                                       (ECompEditorPageAttachments *page_attachments);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PAGE_ATTACHMENTS_H */
diff --git a/calendar/gui/e-comp-editor-page-general.c b/calendar/gui/e-comp-editor-page-general.c
new file mode 100644
index 0000000..965c6da
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-general.c
@@ -0,0 +1,1844 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+#include <camel/camel.h>
+#include <e-util/e-util.h>
+
+#include "comp-util.h"
+#include "e-comp-editor.h"
+#include "e-comp-editor-page.h"
+#include "e-meeting-list-view.h"
+#include "itip-utils.h"
+
+#include "e-comp-editor-page-general.h"
+
+struct _ECompEditorPageGeneralPrivate {
+       GtkWidget *source_label;
+       GtkWidget *source_combo_box;
+       GtkWidget *organizer_label;
+       GtkWidget *organizer_combo_box;
+       GtkWidget *organizer_hbox;
+       GtkWidget *attendees_button;
+       GtkWidget *attendees_hbox;
+       GtkWidget *attendees_list_view;
+       GtkWidget *attendees_button_box;
+       GtkWidget *attendees_button_add;
+       GtkWidget *attendees_button_edit;
+       GtkWidget *attendees_button_remove;
+
+       gint data_column_width;
+       gchar *source_label_text;
+       gchar *source_extension_name;
+       ESource *select_source;
+       gboolean show_attendees;
+
+       EMeetingStore *meeting_store;
+       GSList *orig_attendees; /* gchar *mail_addresses */
+       gchar *user_delegator;
+};
+
+enum {
+       PROP_0,
+       PROP_DATA_COLUMN_WIDTH,
+       PROP_SOURCE_LABEL,
+       PROP_SOURCE_EXTENSION_NAME,
+       PROP_SELECTED_SOURCE,
+       PROP_SHOW_ATTENDEES
+};
+
+G_DEFINE_TYPE (ECompEditorPageGeneral, e_comp_editor_page_general, E_TYPE_COMP_EDITOR_PAGE)
+
+static void ecep_general_sensitize_widgets (ECompEditorPage *page,
+                                           gboolean force_insensitive);
+
+static void
+ecep_general_set_column_visible (ECompEditorPageGeneral *page_general,
+                                EMeetingStoreColumns column,
+                                gboolean visible)
+{
+       EMeetingListView *meeting_list_view;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       meeting_list_view = E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view);
+       e_meeting_list_view_column_set_visible (meeting_list_view, column, visible);
+}
+
+static void
+action_view_role_cb (GtkToggleAction *action,
+                    ECompEditorPageGeneral *page_general)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       ecep_general_set_column_visible (page_general, E_MEETING_STORE_ROLE_COL,
+               gtk_toggle_action_get_active (action));
+}
+
+static void
+action_view_rsvp_cb (GtkToggleAction *action,
+                    ECompEditorPageGeneral *page_general)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       ecep_general_set_column_visible (page_general, E_MEETING_STORE_RSVP_COL,
+               gtk_toggle_action_get_active (action));
+}
+
+static void
+action_view_status_cb (GtkToggleAction *action,
+                      ECompEditorPageGeneral *page_general)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       ecep_general_set_column_visible (page_general, E_MEETING_STORE_STATUS_COL,
+               gtk_toggle_action_get_active (action));
+}
+
+static void
+action_view_type_cb (GtkToggleAction *action,
+                    ECompEditorPageGeneral *page_general)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       ecep_general_set_column_visible (page_general, E_MEETING_STORE_TYPE_COL,
+               gtk_toggle_action_get_active (action));
+}
+
+static void
+ecep_general_fill_organizer_combo_box (ECompEditorPageGeneral *page_general)
+{
+       GtkComboBoxText *combo_box_text;
+       ECompEditor *comp_editor;
+       EShell *shell;
+       ESourceRegistry *registry;
+       gchar **address_strings;
+       gint ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+       g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box));
+
+       combo_box_text = GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box);
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       shell = e_comp_editor_get_shell (comp_editor);
+       registry = e_shell_get_registry (shell);
+       address_strings = itip_get_user_identities (registry);
+
+       for (ii = 0; address_strings && address_strings[ii]; ii++) {
+               gtk_combo_box_text_append_text (combo_box_text, address_strings[ii]);
+       }
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box_text), 0);
+
+       g_strfreev (address_strings);
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_general_source_combo_box_changed_cb (ESourceComboBox *source_combo_box,
+                                         ECompEditorPageGeneral *page_general)
+{
+       ESource *source;
+
+       g_return_if_fail (E_IS_SOURCE_COMBO_BOX (source_combo_box));
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       source = e_source_combo_box_ref_active (source_combo_box);
+       e_comp_editor_page_general_set_selected_source (page_general, source);
+       g_clear_object (&source);
+}
+
+static void
+ecep_general_attendees_clicked_cb (GtkWidget *widget,
+                                  ECompEditorPageGeneral *page_general)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       e_meeting_list_view_invite_others_dialog (E_MEETING_LIST_VIEW 
(page_general->priv->attendees_list_view));
+}
+
+static void
+ecep_general_attendees_add_clicked_cb (GtkButton *button,
+                                      ECompEditorPageGeneral *page_general)
+{
+       ECompEditor *comp_editor;
+       EMeetingAttendee *attendee;
+       guint32 flags;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       flags = e_comp_editor_get_flags (comp_editor);
+
+       attendee = e_meeting_store_add_attendee_with_defaults (page_general->priv->meeting_store);
+
+       if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0)
+               e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s",
+                       page_general->priv->user_delegator ? page_general->priv->user_delegator : ""));
+
+       e_meeting_list_view_edit (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), attendee);
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_general_attendees_edit_clicked_cb (GtkButton *button,
+                                       ECompEditorPageGeneral *page_general)
+{
+       GtkTreeView *tree_view;
+       GtkTreePath *path = NULL;
+       GtkTreeViewColumn *focus_col;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       tree_view = GTK_TREE_VIEW (page_general->priv->attendees_list_view);
+
+       gtk_tree_view_get_cursor (tree_view, &path, NULL);
+       g_return_if_fail (path != NULL);
+
+       gtk_tree_view_get_cursor (tree_view, &path, &focus_col);
+       gtk_tree_view_set_cursor (tree_view, path, focus_col, TRUE);
+       gtk_tree_path_free (path);
+}
+
+static void
+ecep_general_remove_attendee (ECompEditorPageGeneral *page_general,
+                             EMeetingAttendee *attendee)
+{
+       ECompEditor *comp_editor;
+       gint pos = 0;
+
+       /* If this was a delegatee, no longer delegate */
+       if (e_meeting_attendee_is_set_delfrom (attendee)) {
+               EMeetingAttendee *ib;
+
+               ib = e_meeting_store_find_attendee (page_general->priv->meeting_store, 
e_meeting_attendee_get_delfrom (attendee), &pos);
+               if (ib != NULL) {
+                       ECompEditor *comp_editor;
+                       ECompEditorFlags flags;
+
+                       e_meeting_attendee_set_delto (ib, NULL);
+
+                       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+                       flags = e_comp_editor_get_flags (comp_editor);
+
+                       if (!(flags & E_COMP_EDITOR_FLAG_DELEGATE))
+                               e_meeting_attendee_set_edit_level (ib, E_MEETING_ATTENDEE_EDIT_FULL);
+
+                       g_clear_object (&comp_editor);
+               }
+       }
+
+       /* Handle deleting all attendees in the delegation chain */
+       while (attendee) {
+               EMeetingAttendee *ib = NULL;
+
+               if (e_meeting_attendee_get_delto (attendee)) {
+                       ib = e_meeting_store_find_attendee (page_general->priv->meeting_store,
+                               e_meeting_attendee_get_delto (attendee), NULL);
+               }
+
+               e_meeting_list_view_remove_attendee_from_name_selector (
+                       E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), attendee);
+               e_meeting_store_remove_attendee (page_general->priv->meeting_store, attendee);
+
+               attendee = ib;
+       }
+
+       ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       e_comp_editor_set_changed (comp_editor, TRUE);
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_general_attendees_remove_clicked_cb (GtkButton *button,
+                                         ECompEditorPageGeneral *page_general)
+{
+       EMeetingAttendee *attendee;
+       GtkTreeSelection *selection;
+       GList *paths = NULL, *tmp;
+       GtkTreeIter iter;
+       GtkTreePath *path = NULL;
+       GtkTreeModel *model = NULL;
+       gboolean valid_iter;
+       gchar *address;
+       gint failures = 0;
+       GString *errors = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
+       paths = gtk_tree_selection_get_selected_rows (selection, &model);
+       g_return_if_fail (paths != NULL);
+
+       paths = g_list_reverse (paths);
+
+       for (tmp = paths; tmp; tmp = tmp->next) {
+               path = tmp->data;
+
+               gtk_tree_model_get_iter (model, &iter, path);
+
+               gtk_tree_model_get (model, &iter, E_MEETING_STORE_ADDRESS_COL, &address, -1);
+               attendee = e_meeting_store_find_attendee (E_MEETING_STORE (model), address, NULL);
+
+               if (!attendee) {
+                       if (!errors)
+                               errors = g_string_new ("");
+                       else
+                               g_string_append (errors, "\n");
+                       g_string_append_printf (errors, _("Cannot find attendee '%s' in the list of 
attendees"), address);
+                       failures++;
+               } else if (e_meeting_attendee_get_edit_level (attendee) != E_MEETING_ATTENDEE_EDIT_FULL) {
+                       if (!errors)
+                               errors = g_string_new ("");
+                       else
+                               g_string_append (errors, "\n");
+                       g_string_append_printf (errors, _("Not enough rights to delete attendee '%s'"), 
e_meeting_attendee_get_address (attendee));
+                       failures++;
+               } else {
+                       ecep_general_remove_attendee (page_general, attendee);
+               }
+
+               g_free (address);
+       }
+
+       /* Select closest item after removal */
+       valid_iter = gtk_tree_model_get_iter (model, &iter, path);
+       if (!valid_iter) {
+               gtk_tree_path_prev (path);
+               valid_iter = gtk_tree_model_get_iter (model, &iter, path);
+       }
+
+       if (valid_iter) {
+               gtk_tree_selection_unselect_all (selection);
+               gtk_tree_selection_select_iter (selection, &iter);
+       }
+
+       g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+       if (errors) {
+               ECompEditor *comp_editor;
+
+               comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+
+               e_comp_editor_add_error (comp_editor, g_dngettext (GETTEXT_PACKAGE,
+                       "Failed to delete selected attendee",
+                       "Failed to delete selected attendees",
+                       failures), errors->str);
+
+               g_string_free (errors, TRUE);
+               g_clear_object (&comp_editor);
+       }
+}
+
+static void
+ecep_general_attendees_selection_changed_cb (GtkTreeSelection *selection,
+                                            ECompEditorPageGeneral *page_general)
+{
+       g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
+}
+
+static void
+ecep_general_attendee_added_cb (EMeetingListView *meeting_list_view,
+                               EMeetingAttendee *attendee,
+                               ECompEditorPageGeneral *page_general)
+{
+       ECompEditor *comp_editor;
+       ECompEditorFlags flags;
+       ECalClient *client;
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       flags = e_comp_editor_get_flags (comp_editor);
+
+       e_comp_editor_set_changed (comp_editor, TRUE);
+
+       if (!(flags & E_COMP_EDITOR_FLAG_DELEGATE)) {
+               g_clear_object (&comp_editor);
+               return;
+       }
+
+       client = e_comp_editor_get_target_client (comp_editor);
+
+       /* do not remove here, it did EMeetingListView already */
+       e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s",
+               page_general->priv->user_delegator ? page_general->priv->user_delegator : ""));
+
+       if (client && !e_client_check_capability (E_CLIENT (client), CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY)) 
{
+               EMeetingAttendee *delegator;
+
+               delegator = e_meeting_store_find_attendee (page_general->priv->meeting_store,
+                       page_general->priv->user_delegator, NULL);
+               g_return_if_fail (delegator != NULL);
+
+               e_meeting_attendee_set_delto (delegator, g_strdup (e_meeting_attendee_get_address 
(attendee)));
+       }
+
+       ecep_general_sensitize_widgets (E_COMP_EDITOR_PAGE (page_general), FALSE);
+
+       g_clear_object (&comp_editor);
+}
+
+static gboolean
+ecep_general_get_organizer (ECompEditorPageGeneral *page_general,
+                           gchar **out_name,
+                           gchar **out_mailto)
+{
+       gchar *organizer_text;
+       gboolean valid = FALSE;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
+
+       organizer_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT 
(page_general->priv->organizer_combo_box));
+       if (organizer_text) {
+               CamelInternetAddress *address;
+               const gchar *str_name, *str_address;
+
+               address = camel_internet_address_new ();
+               if (camel_address_unformat (CAMEL_ADDRESS (address), organizer_text) == 1 &&
+                   camel_internet_address_get (address, 0, &str_name, &str_address)) {
+                       valid = TRUE;
+
+                       if (out_name)
+                               *out_name = g_strdup (str_name);
+                       if (out_mailto)
+                               *out_mailto = g_strconcat ("MAILTO:", itip_strip_mailto (str_address), NULL);
+               }
+
+               g_object_unref (address);
+               g_free (organizer_text);
+       }
+
+       return valid;
+}
+
+static gboolean
+ecep_general_pick_organizer_for_email_address (ECompEditorPageGeneral *page_general,
+                                              const gchar *email_address)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       GtkComboBox *combo_box;
+       gint ii, entry_text_column;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
+
+       email_address = itip_strip_mailto (email_address);
+
+       if (!email_address || !*email_address)
+               return FALSE;
+
+       combo_box = GTK_COMBO_BOX (page_general->priv->organizer_combo_box);
+       model = gtk_combo_box_get_model (combo_box);
+       entry_text_column = gtk_combo_box_get_entry_text_column (combo_box);
+
+       if (!gtk_tree_model_get_iter_first (model, &iter))
+               return FALSE;
+
+       ii = 0;
+
+       do {
+               gchar *value = NULL;
+
+               gtk_tree_model_get (model, &iter, entry_text_column, &value, -1);
+
+               if (value && g_strrstr (value, email_address)) {
+                       g_free (value);
+                       gtk_combo_box_set_active (combo_box, ii);
+                       return TRUE;
+               }
+
+               g_free (value);
+
+               ii++;
+       } while (gtk_tree_model_iter_next (model, &iter));
+
+       return FALSE;
+}
+
+static void
+ecep_general_target_client_notify_cb (ECompEditor *comp_editor,
+                                     GParamSpec *param,
+                                     ECompEditorPageGeneral *page_general)
+{
+       const gchar *cal_email_address;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
+       ecep_general_pick_organizer_for_email_address (page_general, cal_email_address);
+}
+
+static gboolean
+ecep_general_list_view_event_cb (EMeetingListView *list_view,
+                                GdkEvent *event,
+                                ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (list_view), FALSE);
+       g_return_val_if_fail (event != NULL, FALSE);
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
+
+       if (event->type == GDK_2BUTTON_PRESS && gtk_widget_get_sensitive (GTK_WIDGET (list_view)) &&
+           gtk_widget_get_sensitive (page_general->priv->attendees_button_add)) {
+               EMeetingAttendee *attendee;
+               ECompEditor *comp_editor;
+               guint32 flags;
+
+               attendee = e_meeting_store_add_attendee_with_defaults (page_general->priv->meeting_store);
+
+               comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+               flags = e_comp_editor_get_flags (comp_editor);
+
+               if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) {
+                       e_meeting_attendee_set_delfrom (attendee, g_strdup_printf ("MAILTO:%s", 
page_general->priv->user_delegator));
+               }
+
+               g_clear_object (&comp_editor);
+               e_meeting_list_view_edit (list_view, attendee);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+ecep_general_list_view_key_press_cb (EMeetingListView *list_view,
+                                    GdkEventKey *event,
+                                    ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (list_view), FALSE);
+       g_return_val_if_fail (event != NULL, FALSE);
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
+
+       if (event->keyval == GDK_KEY_Delete) {
+               if (gtk_widget_get_sensitive (page_general->priv->attendees_button_remove))
+                       ecep_general_attendees_remove_clicked_cb (NULL, page_general);
+               return TRUE;
+       } else if (event->keyval == GDK_KEY_Insert) {
+               if (gtk_widget_get_sensitive (page_general->priv->attendees_button_add))
+                       ecep_general_attendees_add_clicked_cb (NULL, page_general);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+ecep_general_init_ui (ECompEditorPageGeneral *page_general,
+                     ECompEditor *comp_editor)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='view-menu'>"
+               "      <placeholder name='columns'>"
+               "        <menuitem action='view-role'/>"
+               "        <menuitem action='view-rsvp'/>"
+               "        <menuitem action='view-status'/>"
+               "        <menuitem action='view-type'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='toggles'>"
+               "        <menuitem action='option-attendees'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "</ui>";
+
+       const GtkToggleActionEntry attendees_toggle_entry[] = {
+
+               { "option-attendees",
+                 NULL,
+                 N_("A_ttendees"),
+                 NULL,
+                 N_("Toggles whether the Attendees are displayed"),
+                 NULL,
+                 FALSE }
+       };
+
+       const GtkToggleActionEntry columns_toggle_entries[] = {
+
+               { "view-role",
+                 NULL,
+                 N_("R_ole Field"),
+                 NULL,
+                 N_("Toggles whether the Role field is displayed"),
+                 G_CALLBACK (action_view_role_cb),
+                 TRUE },
+
+               { "view-rsvp",
+                 NULL,
+                 N_("_RSVP"),
+                 NULL,
+                 N_("Toggles whether the RSVP field is displayed"),
+                 G_CALLBACK (action_view_rsvp_cb),
+                 TRUE },
+
+               { "view-status",
+                 NULL,
+                 N_("_Status Field"),
+                 NULL,
+                 N_("Toggles whether the Status field is displayed"),
+                 G_CALLBACK (action_view_status_cb),
+                 TRUE },
+
+               { "view-type",
+                 NULL,
+                 N_("_Type Field"),
+                 NULL,
+                 N_("Toggles whether the Attendee Type is displayed"),
+                 G_CALLBACK (action_view_type_cb),
+                 TRUE }
+       };
+
+       GtkUIManager *ui_manager;
+       GtkActionGroup *action_group;
+       GtkAction *action;
+       GSettings *settings;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       settings = e_comp_editor_get_settings (comp_editor);
+       ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+
+       action_group = gtk_action_group_new ("columns");
+       gtk_action_group_set_translation_domain (
+               action_group, GETTEXT_PACKAGE);
+       gtk_action_group_add_toggle_actions (
+               action_group, columns_toggle_entries,
+               G_N_ELEMENTS (columns_toggle_entries), page_general);
+       gtk_ui_manager_insert_action_group (
+               ui_manager, action_group, 0);
+
+       e_binding_bind_property (
+               page_general, "show-attendees",
+               action_group, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       g_object_unref (action_group);
+
+       action_group = e_comp_editor_get_action_group (comp_editor, "individual");
+       gtk_action_group_add_toggle_actions (
+               action_group, attendees_toggle_entry,
+               G_N_ELEMENTS (attendees_toggle_entry), page_general);
+
+       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);
+       }
+
+       action = e_comp_editor_get_action (comp_editor, "option-attendees");
+       e_binding_bind_property (
+               page_general, "show-attendees",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+       action = e_comp_editor_get_action (comp_editor, "view-role");
+       g_settings_bind (
+               settings, "editor-show-role",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+
+       action = e_comp_editor_get_action (comp_editor, "view-rsvp");
+       g_settings_bind (
+               settings, "editor-show-rsvp",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+
+       action = e_comp_editor_get_action (comp_editor, "view-status");
+       g_settings_bind (
+               settings, "editor-show-status",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+
+       action = e_comp_editor_get_action (comp_editor, "view-type");
+       g_settings_bind (
+               settings, "editor-show-type",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+ecep_general_sensitize_widgets (ECompEditorPage *page,
+                               gboolean force_insensitive)
+{
+       ECompEditorPageGeneral *page_general;
+       GtkTreeSelection *selection;
+       gboolean sensitive, organizer_is_user, delegate, delegate_to_many = FALSE, read_only = TRUE, 
any_selected = FALSE;
+       ECompEditor *comp_editor;
+       ECalClient *client;
+       guint32 flags;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->sensitize_widgets (page, 
force_insensitive);
+
+       page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       flags = e_comp_editor_get_flags (comp_editor);
+       client = e_comp_editor_get_target_client (comp_editor);
+
+       if (client) {
+               EClient *cl = E_CLIENT (client);
+
+               read_only = e_client_is_readonly (cl);
+               delegate_to_many = e_client_check_capability (cl, CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY);
+       } else {
+               force_insensitive = TRUE;
+       }
+
+       delegate = (flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0;
+       organizer_is_user = (flags & (E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER | E_COMP_EDITOR_FLAG_IS_NEW)) != 0 
||
+               !e_comp_editor_page_general_get_show_attendees (page_general);
+       sensitive = (!read_only && organizer_is_user) || delegate;
+
+       if (!delegate)
+               delegate_to_many = TRUE;
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
+       any_selected = selection && gtk_tree_selection_count_selected_rows (selection) > 0;
+
+       gtk_widget_set_sensitive (page_general->priv->organizer_label, !force_insensitive);
+       gtk_widget_set_sensitive (page_general->priv->organizer_combo_box, !read_only && !force_insensitive);
+       gtk_widget_set_sensitive (page_general->priv->attendees_button, sensitive && delegate_to_many && 
!force_insensitive);
+       gtk_widget_set_sensitive (page_general->priv->attendees_hbox, !force_insensitive);
+       gtk_widget_set_sensitive (page_general->priv->attendees_button_add, sensitive && delegate_to_many && 
!force_insensitive);
+       gtk_widget_set_sensitive (page_general->priv->attendees_button_edit, sensitive && delegate_to_many && 
!force_insensitive && any_selected);
+       gtk_widget_set_sensitive (page_general->priv->attendees_button_remove, sensitive && 
!force_insensitive && any_selected);
+       e_meeting_list_view_set_editable (E_MEETING_LIST_VIEW (page_general->priv->attendees_list_view), 
sensitive && !force_insensitive);
+       gtk_widget_set_sensitive (page_general->priv->attendees_list_view, !read_only && !force_insensitive);
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_general_fill_widgets (ECompEditorPage *page,
+                          icalcomponent *component)
+{
+       ECompEditorPageGeneral *page_general;
+       icalproperty *prop;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page));
+       g_return_if_fail (component != NULL);
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->fill_widgets (page, component);
+
+       page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
+
+       g_slist_free_full (page_general->priv->orig_attendees, g_free);
+       page_general->priv->orig_attendees = NULL;
+
+       for (prop = icalcomponent_get_first_property (component, ICAL_ATTENDEE_PROPERTY);
+            prop;
+            prop = icalcomponent_get_next_property (component, ICAL_ATTENDEE_PROPERTY)) {
+               const gchar *address;
+
+               address = itip_strip_mailto (icalproperty_get_attendee (prop));
+               if (address)
+                       page_general->priv->orig_attendees = g_slist_prepend 
(page_general->priv->orig_attendees, g_strdup (address));
+       }
+
+       page_general->priv->orig_attendees = g_slist_reverse (page_general->priv->orig_attendees);
+
+       prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY);
+       if (prop) {
+               icalparameter *param;
+               const gchar *organizer;
+
+               organizer = icalproperty_get_organizer (prop);
+
+               if (organizer && *organizer) {
+                       ECompEditor *comp_editor;
+                       ESourceRegistry *registry;
+                       GtkEntry *combo_box_entry;
+                       guint32 flags;
+                       gchar *value = NULL;
+
+                       comp_editor = e_comp_editor_page_ref_editor (page);
+                       flags = e_comp_editor_get_flags (comp_editor);
+                       registry = e_shell_get_registry (e_comp_editor_get_shell (comp_editor));
+
+                       if (itip_address_is_user (registry, itip_strip_mailto (organizer))) {
+                               flags = flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
+                       } else {
+                               param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+                               if (param) {
+                                       const gchar *sentby = icalparameter_get_sentby (param);
+
+                                       if (sentby && *sentby &&
+                                           itip_address_is_user (registry, itip_strip_mailto (organizer))) {
+                                               flags = flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
+                                       }
+                               }
+                       }
+
+                       e_comp_editor_page_general_set_show_attendees (page_general, TRUE);
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+                       if (param) {
+                               const gchar *cn;
+
+                               cn = icalparameter_get_cn (param);
+                               if (cn && *cn) {
+                                       value = g_strdup_printf ("%s <%s>", cn, itip_strip_mailto 
(organizer));
+                               }
+                       }
+
+                       if (!value)
+                               value = g_strdup (itip_strip_mailto (organizer));
+
+                       combo_box_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN 
(page_general->priv->organizer_combo_box)));
+
+                       if (!(flags & E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER)) {
+                               GtkComboBoxText *combo_box_text;
+
+                               combo_box_text = GTK_COMBO_BOX_TEXT (page_general->priv->organizer_combo_box);
+                               gtk_combo_box_text_remove_all (combo_box_text);
+                               gtk_combo_box_text_append_text (combo_box_text, value);
+                               gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box_text), 0);
+                               gtk_editable_set_editable (GTK_EDITABLE (combo_box_entry), FALSE);
+                       } else if (!ecep_general_pick_organizer_for_email_address (page_general, organizer)) {
+                               gtk_entry_set_text (combo_box_entry, value);
+                       }
+
+                       g_clear_object (&comp_editor);
+                       g_free (value);
+               }
+       }
+
+       e_meeting_store_remove_all_attendees (page_general->priv->meeting_store);
+
+       for (prop = icalcomponent_get_first_property (component, ICAL_ATTENDEE_PROPERTY);
+            prop;
+            prop = icalcomponent_get_next_property (component, ICAL_ATTENDEE_PROPERTY)) {
+               const gchar *address;
+
+               address = itip_strip_mailto (icalproperty_get_attendee (prop));
+               if (address) {
+                       EMeetingAttendee *attendee;
+                       icalparameter *param;
+
+                       attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new ());
+
+                       e_meeting_attendee_set_address (attendee, g_strdup (address));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_MEMBER_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_member (attendee, g_strdup (icalparameter_get_member 
(param)));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_CUTYPE_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_cutype (attendee, icalparameter_get_cutype (param));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_ROLE_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_role (attendee, icalparameter_get_role (param));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_rsvp (attendee, icalparameter_get_rsvp (param) == 
ICAL_RSVP_TRUE);
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDTO_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_delto (attendee, g_strdup 
(icalparameter_get_delegatedto (param)));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_delfrom (attendee, g_strdup 
(icalparameter_get_delegatedfrom (param)));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_status (attendee, icalparameter_get_partstat (param));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_sentby (attendee, g_strdup (icalparameter_get_sentby 
(param)));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_cn (attendee, g_strdup (icalparameter_get_cn (param)));
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_LANGUAGE_PARAMETER);
+                       if (param)
+                               e_meeting_attendee_set_language (attendee, g_strdup 
(icalparameter_get_language (param)));
+
+                       e_meeting_store_add_attendee (page_general->priv->meeting_store, attendee);
+
+                       g_object_unref (attendee);
+               }
+       }
+}
+
+static gboolean
+ecep_general_fill_component (ECompEditorPage *page,
+                            icalcomponent *component)
+{
+       ECompEditorPageGeneral *page_general;
+       icalproperty *prop;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
+
+       cal_comp_util_remove_all_properties (component, ICAL_ATTENDEE_PROPERTY);
+
+       if (e_comp_editor_page_general_get_show_attendees (page_general)) {
+               const GPtrArray *attendees;
+               GHashTable *known_attendees;
+               ECompEditor *comp_editor;
+               gchar *organizer_name = NULL, *organizer_mailto = NULL;
+               guint32 flags;
+               gint ii, added_attendees = 0;
+
+               comp_editor = e_comp_editor_page_ref_editor (page);
+               flags = e_comp_editor_get_flags (comp_editor);
+
+               if ((flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0 &&
+                   !ecep_general_get_organizer (page_general, NULL, NULL)) {
+                       e_comp_editor_set_validation_error (comp_editor, page,
+                               page_general->priv->organizer_combo_box,
+                               _("An organizer is required."));
+
+                       g_clear_object (&comp_editor);
+
+                       return FALSE;
+               }
+
+               if (e_meeting_store_count_actual_attendees (page_general->priv->meeting_store) < 1) {
+                       e_comp_editor_set_validation_error (comp_editor, page,
+                               page_general->priv->attendees_list_view,
+                               _("At least one attendee is required."));
+
+                       g_clear_object (&comp_editor);
+
+                       return FALSE;
+               }
+
+               /* Organizer */
+               if ((flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0 &&
+                   ecep_general_get_organizer (page_general, &organizer_name, &organizer_mailto)) {
+                       const gchar *cal_email_address;
+                       icalparameter *param;
+
+                       prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY);
+                       if (!prop) {
+                               prop = icalproperty_new_organizer (organizer_mailto ? organizer_mailto : 
organizer_name);
+                               icalcomponent_add_property (component, prop);
+                       } else {
+                               icalproperty_set_organizer (prop, organizer_mailto ? organizer_mailto : 
organizer_name);
+                       }
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+                       if (organizer_name && *organizer_name) {
+                               if (!param) {
+                                       param = icalparameter_new_cn (organizer_name);
+                                       icalproperty_add_parameter (prop, param);
+                               } else {
+                                       icalparameter_set_cn (param, organizer_name);
+                               }
+                       } else if (param) {
+                               icalproperty_remove_parameter (prop, ICAL_CN_PARAMETER);
+                       }
+
+                       param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+                       cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
+                       if (cal_email_address && *cal_email_address) {
+                               gchar *sentby;
+                               gboolean differs;
+
+                               sentby = g_strconcat ("MAILTO:", cal_email_address, NULL);
+                               differs = !organizer_mailto || g_ascii_strcasecmp (sentby, organizer_mailto) 
!= 0;
+
+                               if (differs) {
+                                       if (!param) {
+                                               param = icalparameter_new_sentby (sentby);
+                                               icalproperty_add_parameter (prop, param);
+                                       } else {
+                                               icalparameter_set_sentby (param, sentby);
+                                       }
+                               } else if (param) {
+                                       icalproperty_remove_parameter (prop, ICAL_SENTBY_PARAMETER);
+                               }
+
+                               g_free (sentby);
+                       } else if (param) {
+                               icalproperty_remove_parameter (prop, ICAL_SENTBY_PARAMETER);
+                       }
+               }
+
+               /* Attendees */
+               known_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+               attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
+
+               for (ii = 0; ii < attendees->len; ii++) {
+                       EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
+                       const gchar *address;
+
+                       address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
+                       if (address) {
+                               icalparameter *param;
+
+                               if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0 &&
+                                   (e_meeting_attendee_is_set_delfrom (attendee) || 
e_meeting_attendee_is_set_delto (attendee)) &&
+                                   g_hash_table_contains (known_attendees, address))
+                                       continue;
+
+                               g_hash_table_insert (known_attendees, (gpointer) address, GINT_TO_POINTER 
(1));
+
+                               prop = icalproperty_new_attendee (e_meeting_attendee_get_address (attendee));
+                               icalcomponent_add_property (component, prop);
+
+                               added_attendees++;
+
+                               if (e_meeting_attendee_is_set_member (attendee)) {
+                                       param = icalparameter_new_member (e_meeting_attendee_get_member 
(attendee));
+                                       icalproperty_add_parameter (prop, param);
+                               }
+
+                               param = icalparameter_new_cutype (e_meeting_attendee_get_cutype (attendee));
+                               icalproperty_add_parameter (prop, param);
+
+                               param = icalparameter_new_role (e_meeting_attendee_get_role (attendee));
+                               icalproperty_add_parameter (prop, param);
+
+                               param = icalparameter_new_partstat (e_meeting_attendee_get_status (attendee));
+                               icalproperty_add_parameter (prop, param);
+
+                               param = icalparameter_new_rsvp (e_meeting_attendee_get_rsvp (attendee) ? 
ICAL_RSVP_TRUE : ICAL_RSVP_FALSE);
+                               icalproperty_add_parameter (prop, param);
+
+                               if (e_meeting_attendee_is_set_delfrom (attendee)) {
+                                       param = icalparameter_new_delegatedfrom 
(e_meeting_attendee_get_delfrom (attendee));
+                                       icalproperty_add_parameter (prop, param);
+                               }
+                               if (e_meeting_attendee_is_set_delto (attendee)) {
+                                       param = icalparameter_new_delegatedto (e_meeting_attendee_get_delto 
(attendee));
+                                       icalproperty_add_parameter (prop, param);
+                               }
+                               if (e_meeting_attendee_is_set_sentby (attendee)) {
+                                       param = icalparameter_new_sentby (e_meeting_attendee_get_sentby 
(attendee));
+                                       icalproperty_add_parameter (prop, param);
+                               }
+                               if (e_meeting_attendee_is_set_cn (attendee)) {
+                                       param = icalparameter_new_cn (e_meeting_attendee_get_cn (attendee));
+                                       icalproperty_add_parameter (prop, param);
+                               }
+                               if (e_meeting_attendee_is_set_language (attendee)) {
+                                       param = icalparameter_new_language (e_meeting_attendee_get_language 
(attendee));
+                                       icalproperty_add_parameter (prop, param);
+                               }
+                       }
+               }
+
+               g_hash_table_destroy (known_attendees);
+
+               g_free (organizer_name);
+               g_free (organizer_mailto);
+
+               if (!added_attendees) {
+                       e_comp_editor_set_validation_error (comp_editor, page,
+                               page_general->priv->attendees_list_view,
+                               _("At least one attendee is required."));
+
+                       g_clear_object (&comp_editor);
+
+                       return FALSE;
+               }
+
+               g_clear_object (&comp_editor);
+       } else {
+               cal_comp_util_remove_all_properties (component, ICAL_ORGANIZER_PROPERTY);
+       }
+
+       return E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_general_parent_class)->fill_component (page, 
component);
+}
+
+static void
+ecep_general_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_DATA_COLUMN_WIDTH:
+                       e_comp_editor_page_general_set_data_column_width (
+                               E_COMP_EDITOR_PAGE_GENERAL (object),
+                               g_value_get_int (value));
+                       return;
+
+               case PROP_SOURCE_LABEL:
+                       e_comp_editor_page_general_set_source_label (
+                               E_COMP_EDITOR_PAGE_GENERAL (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_SOURCE_EXTENSION_NAME:
+                       e_comp_editor_page_general_set_source_extension_name (
+                               E_COMP_EDITOR_PAGE_GENERAL (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_SELECTED_SOURCE:
+                       e_comp_editor_page_general_set_selected_source (
+                               E_COMP_EDITOR_PAGE_GENERAL (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_SHOW_ATTENDEES:
+                       e_comp_editor_page_general_set_show_attendees (
+                               E_COMP_EDITOR_PAGE_GENERAL (object),
+                               g_value_get_boolean (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecep_general_get_property (GObject *object,
+                          guint property_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_DATA_COLUMN_WIDTH:
+                       g_value_set_int (
+                               value,
+                               e_comp_editor_page_general_get_data_column_width (
+                               E_COMP_EDITOR_PAGE_GENERAL (object)));
+                       return;
+
+               case PROP_SOURCE_LABEL:
+                       g_value_set_string (
+                               value,
+                               e_comp_editor_page_general_get_source_label (
+                               E_COMP_EDITOR_PAGE_GENERAL (object)));
+                       return;
+
+               case PROP_SOURCE_EXTENSION_NAME:
+                       g_value_set_string (
+                               value,
+                               e_comp_editor_page_general_get_source_extension_name (
+                               E_COMP_EDITOR_PAGE_GENERAL (object)));
+                       return;
+
+               case PROP_SELECTED_SOURCE:
+                       g_value_take_object (
+                               value,
+                               e_comp_editor_page_general_ref_selected_source (
+                               E_COMP_EDITOR_PAGE_GENERAL (object)));
+                       return;
+
+               case PROP_SHOW_ATTENDEES:
+                       g_value_set_boolean (
+                               value,
+                               e_comp_editor_page_general_get_show_attendees (
+                               E_COMP_EDITOR_PAGE_GENERAL (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecep_general_constructed (GObject *object)
+{
+       ECompEditor *comp_editor;
+       ECompEditorPageGeneral *page_general;
+       GtkWidget *widget, *scrolled_window;
+       GtkTreeSelection *selection;
+       GtkGrid *grid;
+       EShell *shell;
+
+       page_general = E_COMP_EDITOR_PAGE_GENERAL (object);
+
+       G_OBJECT_CLASS (e_comp_editor_page_general_parent_class)->constructed (object);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       g_return_if_fail (comp_editor != NULL);
+
+       page_general->priv->meeting_store = E_MEETING_STORE (e_meeting_store_new ());
+
+       grid = GTK_GRID (page_general);
+
+       widget = gtk_label_new_with_mnemonic (_("Or_ganizer:"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+       gtk_widget_hide (widget);
+
+       page_general->priv->organizer_label = widget;
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_grid_attach (grid, widget, 1, 0, page_general->priv->data_column_width, 1);
+       gtk_widget_hide (widget);
+
+       page_general->priv->organizer_hbox = widget;
+
+       widget = gtk_combo_box_text_new_with_entry ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->organizer_combo_box = widget;
+
+       /* The list of organizers is set to be non-editable. Otherwise any
+        * change in the displayed list causes an 'Account not found' error.
+        */
+       gtk_editable_set_editable (GTK_EDITABLE (gtk_bin_get_child (GTK_BIN (widget))), FALSE);
+
+       ecep_general_fill_organizer_combo_box (page_general);
+
+       g_signal_connect_swapped (page_general->priv->organizer_combo_box, "changed",
+               G_CALLBACK (e_comp_editor_ensure_changed), comp_editor);
+
+       widget = gtk_label_new_with_mnemonic (page_general->priv->source_label_text);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->source_label = widget;
+
+       shell = e_comp_editor_get_shell (comp_editor);
+       widget = e_source_combo_box_new (
+               e_shell_get_registry (shell),
+               page_general->priv->source_extension_name);
+       e_source_combo_box_set_show_colors (E_SOURCE_COMBO_BOX (widget), TRUE);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox), widget, TRUE, TRUE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->source_combo_box = widget;
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (page_general->priv->source_label),
+               page_general->priv->source_combo_box);
+       g_signal_connect (page_general->priv->source_combo_box, "changed",
+               G_CALLBACK (ecep_general_source_combo_box_changed_cb), page_general);
+
+       widget = gtk_button_new_with_mnemonic (C_("ECompEditor", "Atte_ndees..."));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+       gtk_widget_hide (widget);
+
+       page_general->priv->attendees_button = widget;
+
+       g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_clicked_cb), page_general);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_grid_attach (grid, widget, 1, 1, page_general->priv->data_column_width, 1);
+       gtk_widget_hide (widget);
+
+       page_general->priv->attendees_hbox = widget;
+
+       widget = GTK_WIDGET (e_meeting_list_view_new (page_general->priv->meeting_store));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+
+       scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, 
GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
+       gtk_widget_show (scrolled_window);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+       gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_hbox), scrolled_window, TRUE, TRUE, 0);
+
+       page_general->priv->attendees_list_view = widget;
+
+       g_signal_connect (page_general->priv->attendees_list_view, "attendee-added",
+               G_CALLBACK (ecep_general_attendee_added_cb), page_general);
+
+       g_signal_connect (page_general->priv->attendees_list_view, "event",
+               G_CALLBACK (ecep_general_list_view_event_cb), page_general);
+
+       g_signal_connect (page_general->priv->attendees_list_view, "key_press_event",
+               G_CALLBACK (ecep_general_list_view_key_press_cb), page_general);
+
+       widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_hbox), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->attendees_button_box = widget;
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_general->priv->attendees_list_view));
+       g_signal_connect (selection, "changed", G_CALLBACK (ecep_general_attendees_selection_changed_cb), 
page_general);
+
+       widget = gtk_button_new_with_mnemonic (_("_Add"));
+       gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->attendees_button_add = widget;
+
+       g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_add_clicked_cb), 
page_general);
+
+       widget = gtk_button_new_with_mnemonic (_("_Edit"));
+       gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->attendees_button_edit = widget;
+
+       g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_edit_clicked_cb), 
page_general);
+
+       widget = gtk_button_new_with_mnemonic (_("_Remove"));
+       gtk_box_pack_start (GTK_BOX (page_general->priv->attendees_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       page_general->priv->attendees_button_remove = widget;
+
+       g_signal_connect (widget, "clicked", G_CALLBACK (ecep_general_attendees_remove_clicked_cb), 
page_general);
+
+       e_signal_connect_notify (comp_editor, "notify::target-client", G_CALLBACK 
(ecep_general_target_client_notify_cb), page_general);
+
+       ecep_general_init_ui (page_general, comp_editor);
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_general_finalize (GObject *object)
+{
+       ECompEditorPageGeneral *page_general;
+
+       page_general = E_COMP_EDITOR_PAGE_GENERAL (object);
+
+       g_free (page_general->priv->source_label_text);
+       page_general->priv->source_label_text = NULL;
+
+       g_free (page_general->priv->source_extension_name);
+       page_general->priv->source_extension_name = NULL;
+
+       g_free (page_general->priv->user_delegator);
+       page_general->priv->user_delegator = NULL;
+
+       g_clear_object (&page_general->priv->select_source);
+       g_clear_object (&page_general->priv->meeting_store);
+
+       g_slist_free_full (page_general->priv->orig_attendees, g_free);
+       page_general->priv->orig_attendees = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_page_general_parent_class)->finalize (object);
+}
+
+static void
+e_comp_editor_page_general_init (ECompEditorPageGeneral *page_general)
+{
+       page_general->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_general,
+               E_TYPE_COMP_EDITOR_PAGE_GENERAL,
+               ECompEditorPageGeneralPrivate);
+}
+
+static void
+e_comp_editor_page_general_class_init (ECompEditorPageGeneralClass *klass)
+{
+       ECompEditorPageClass *page_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPageGeneralPrivate));
+
+       page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
+       page_class->sensitize_widgets = ecep_general_sensitize_widgets;
+       page_class->fill_widgets = ecep_general_fill_widgets;
+       page_class->fill_component = ecep_general_fill_component;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = ecep_general_set_property;
+       object_class->get_property = ecep_general_get_property;
+       object_class->constructed = ecep_general_constructed;
+       object_class->finalize = ecep_general_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_DATA_COLUMN_WIDTH,
+               g_param_spec_int (
+                       "data-column-width",
+                       "Data Column Width",
+                       "How many columns should the data column occupy",
+                       1, G_MAXINT, 1,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SOURCE_LABEL,
+               g_param_spec_string (
+                       "source-label",
+                       "Source Label",
+                       "Label to use for the source selector",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SOURCE_EXTENSION_NAME,
+               g_param_spec_string (
+                       "source-extension-name",
+                       "Source Extension Name",
+                       "Extension name to use for the source selector",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SELECTED_SOURCE,
+               g_param_spec_object (
+                       "selected-source",
+                       "Selected Source",
+                       "Which source is currently selected in the source selector",
+                       E_TYPE_SOURCE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SHOW_ATTENDEES,
+               g_param_spec_boolean (
+                       "show-attendees",
+                       "Show Attendees",
+                       "Whether to show also attendees",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+ECompEditorPage *
+e_comp_editor_page_general_new (ECompEditor *editor,
+                               const gchar *source_label,
+                               const gchar *source_extension_name,
+                               ESource *select_source,
+                               gboolean show_attendees,
+                               gint data_column_width)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
+       g_return_val_if_fail (source_label != NULL, NULL);
+       g_return_val_if_fail (source_extension_name != NULL, NULL);
+
+       if (select_source)
+               g_return_val_if_fail (E_IS_SOURCE (select_source), NULL);
+
+       return g_object_new (E_TYPE_COMP_EDITOR_PAGE_GENERAL,
+               "editor", editor,
+               "source-label", source_label,
+               "source-extension-name", source_extension_name,
+               "selected-source", select_source,
+               "show-attendees", show_attendees,
+               "data-column-width", data_column_width,
+               NULL);
+}
+
+const gchar *
+e_comp_editor_page_general_get_source_label (ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
+
+       if (page_general->priv->source_label)
+               return gtk_label_get_text (GTK_LABEL (page_general->priv->source_label));
+
+       return page_general->priv->source_label_text;
+}
+
+void
+e_comp_editor_page_general_set_source_label (ECompEditorPageGeneral *page_general,
+                                            const gchar *source_label)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+       g_return_if_fail (source_label != NULL);
+
+       if (page_general->priv->source_label) {
+               if (g_strcmp0 (source_label, gtk_label_get_text (GTK_LABEL 
(page_general->priv->source_label))) != 0) {
+                       gtk_label_set_text (GTK_LABEL (page_general->priv->source_label), source_label);
+
+                       g_object_notify (G_OBJECT (page_general), "source-label");
+               }
+       } else {
+               g_free (page_general->priv->source_label_text);
+               page_general->priv->source_label_text = g_strdup (source_label);
+
+               g_object_notify (G_OBJECT (page_general), "source-label");
+       }
+}
+
+const gchar *
+e_comp_editor_page_general_get_source_extension_name (ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
+
+       if (page_general->priv->source_combo_box)
+               return e_source_combo_box_get_extension_name (E_SOURCE_COMBO_BOX 
(page_general->priv->source_combo_box));
+
+       return page_general->priv->source_extension_name;
+}
+
+void
+e_comp_editor_page_general_set_source_extension_name (ECompEditorPageGeneral *page_general,
+                                                     const gchar *source_extension_name)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       if (g_strcmp0 (page_general->priv->source_extension_name, source_extension_name) == 0)
+               return;
+
+       g_free (page_general->priv->source_extension_name);
+       page_general->priv->source_extension_name = g_strdup (source_extension_name);
+
+       g_object_notify (G_OBJECT (page_general), "source-extension-name");
+
+       if (page_general->priv->source_combo_box) {
+               e_source_combo_box_set_extension_name (
+                       E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box),
+                       source_extension_name);
+       }
+}
+
+ESource *
+e_comp_editor_page_general_ref_selected_source (ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
+       g_return_val_if_fail (page_general->priv->source_combo_box != NULL, NULL);
+
+       return e_source_combo_box_ref_active (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box));
+}
+
+void
+e_comp_editor_page_general_set_selected_source (ECompEditorPageGeneral *page_general,
+                                               ESource *source)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+       if (source)
+               g_return_if_fail (E_IS_SOURCE (source));
+
+       if (!page_general->priv->source_combo_box) {
+               g_clear_object (&page_general->priv->select_source);
+               page_general->priv->select_source = g_object_ref (source);
+
+               g_object_notify (G_OBJECT (page_general), "selected-source");
+
+               return;
+       }
+
+       if (source)
+               e_source_combo_box_set_active (E_SOURCE_COMBO_BOX (page_general->priv->source_combo_box), 
source);
+
+       g_object_notify (G_OBJECT (page_general), "selected-source");
+}
+
+gboolean
+e_comp_editor_page_general_get_show_attendees (ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), FALSE);
+
+       return page_general->priv->show_attendees;
+}
+
+void
+e_comp_editor_page_general_set_show_attendees (ECompEditorPageGeneral *page_general,
+                                              gboolean show_attendees)
+{
+       ECompEditor *comp_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       if ((show_attendees ? 1 : 0) == (page_general->priv->show_attendees ? 1 : 0))
+               return;
+
+       page_general->priv->show_attendees = show_attendees;
+
+       g_object_notify (G_OBJECT (page_general), "show-attendees");
+
+       e_comp_editor_page_general_update_view (page_general);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_general));
+       if (comp_editor)
+               e_comp_editor_set_changed (comp_editor, TRUE);
+       g_clear_object (&comp_editor);
+}
+
+gint
+e_comp_editor_page_general_get_data_column_width (ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), 0);
+
+       return page_general->priv->data_column_width;
+}
+
+void
+e_comp_editor_page_general_set_data_column_width (ECompEditorPageGeneral *page_general,
+                                                 gint data_column_width)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       if (data_column_width == page_general->priv->data_column_width)
+               return;
+
+       page_general->priv->data_column_width = data_column_width;
+
+       g_object_notify (G_OBJECT (page_general), "data-column-width");
+
+       e_comp_editor_page_general_update_view (page_general);
+}
+
+void
+e_comp_editor_page_general_update_view (ECompEditorPageGeneral *page_general)
+{
+       GtkContainer *grid;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+
+       if (!page_general->priv->source_label)
+               return;
+
+       grid = GTK_CONTAINER (page_general);
+
+       gtk_container_child_set (grid, page_general->priv->organizer_hbox,
+               "left-attach", 1, "width", page_general->priv->data_column_width, NULL);
+       gtk_container_child_set (grid, page_general->priv->attendees_hbox,
+               "width", page_general->priv->data_column_width, NULL);
+
+       if (page_general->priv->show_attendees) {
+               if (gtk_widget_get_parent (page_general->priv->source_label) == GTK_WIDGET (grid)) {
+                       g_object_ref (page_general->priv->source_label);
+                       g_object_ref (page_general->priv->source_combo_box);
+
+                       gtk_container_remove (grid, page_general->priv->source_label);
+                       gtk_container_remove (grid, page_general->priv->source_combo_box);
+
+                       gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox),
+                               page_general->priv->source_label, FALSE, FALSE, 0);
+                       gtk_box_pack_start (GTK_BOX (page_general->priv->organizer_hbox),
+                               page_general->priv->source_combo_box, TRUE, TRUE, 0);
+
+                       g_object_unref (page_general->priv->source_label);
+                       g_object_unref (page_general->priv->source_combo_box);
+               }
+
+               gtk_container_child_set (grid, page_general->priv->organizer_label,
+                       "left-attach", 0, NULL);
+
+               gtk_widget_show (page_general->priv->organizer_label);
+               gtk_widget_show (page_general->priv->organizer_hbox);
+               gtk_widget_show (page_general->priv->attendees_button);
+               gtk_widget_show (page_general->priv->attendees_hbox);
+               gtk_widget_show (page_general->priv->attendees_list_view);
+               gtk_widget_show (page_general->priv->attendees_button_box);
+       } else {
+               if (gtk_widget_get_parent (page_general->priv->source_label) != GTK_WIDGET (grid)) {
+                       GtkContainer *container;
+                       GtkGrid *ggrid;
+
+                       container = GTK_CONTAINER (page_general->priv->organizer_hbox);
+                       ggrid = GTK_GRID (grid);
+
+                       g_object_ref (page_general->priv->source_label);
+                       g_object_ref (page_general->priv->source_combo_box);
+
+                       gtk_container_remove (container, page_general->priv->source_label);
+                       gtk_container_remove (container, page_general->priv->source_combo_box);
+
+                       gtk_grid_attach (ggrid, page_general->priv->source_label, 0, 0, 1, 1);
+                       gtk_grid_attach (ggrid, page_general->priv->source_combo_box, 1, 0, 1, 1);
+
+                       g_object_unref (page_general->priv->source_label);
+                       g_object_unref (page_general->priv->source_combo_box);
+               }
+
+               gtk_container_child_set (grid, page_general->priv->source_label,
+                       "left-attach", 0, NULL);
+               gtk_container_child_set (grid, page_general->priv->source_combo_box,
+                       "left-attach", 1, "width", page_general->priv->data_column_width, NULL);
+
+               gtk_widget_hide (page_general->priv->organizer_label);
+               gtk_widget_hide (page_general->priv->organizer_hbox);
+               gtk_widget_hide (page_general->priv->attendees_button);
+               gtk_widget_hide (page_general->priv->attendees_hbox);
+               gtk_widget_hide (page_general->priv->attendees_list_view);
+               gtk_widget_hide (page_general->priv->attendees_button_box);
+       }
+}
+
+EMeetingStore *
+e_comp_editor_page_general_get_meeting_store (ECompEditorPageGeneral *page_general)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
+
+       return page_general->priv->meeting_store;
+}
+
+/* Element is a string, an email address; free with g_slist_free_full (slist, g_free); */
+GSList *
+e_comp_editor_page_general_get_added_attendees (ECompEditorPageGeneral *page_general)
+{
+       const GPtrArray *attendees;
+       GHashTable *orig_attendees = NULL;
+       GSList *added_attendees = NULL, *link;
+       gint ii;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
+
+       if (!page_general->priv->show_attendees)
+               return NULL;
+
+       if (page_general->priv->orig_attendees) {
+               for (link = page_general->priv->orig_attendees; link; link = g_slist_next (link)) {
+                       const gchar *address = link->data;
+
+                       if (address) {
+                               if (!orig_attendees)
+                                       orig_attendees = g_hash_table_new (camel_strcase_hash, 
camel_strcase_equal);
+                               g_hash_table_insert (orig_attendees, (gpointer) address, GINT_TO_POINTER (1));
+                       }
+               }
+       }
+
+       attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
+
+       for (ii = 0; ii < attendees->len; ii++) {
+               EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
+               const gchar *address;
+
+               address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
+
+               if (address && (!orig_attendees || !g_hash_table_contains (orig_attendees, address)))
+                       added_attendees = g_slist_prepend (added_attendees, g_strdup (address));
+       }
+
+       if (orig_attendees)
+               g_hash_table_destroy (orig_attendees);
+
+       return g_slist_reverse (added_attendees);
+}
+
+/* Element is a string, an email address; free with g_slist_free_full (slist, g_free); */
+GSList *
+e_comp_editor_page_general_get_removed_attendees (ECompEditorPageGeneral *page_general)
+{
+       const GPtrArray *attendees;
+       GHashTable *new_attendees;
+       GSList *removed_attendees = NULL, *link;
+       gint ii;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general), NULL);
+
+       if (!page_general->priv->orig_attendees)
+               return NULL;
+
+       if (!page_general->priv->show_attendees) {
+               GSList *copy, *link;
+
+               copy = g_slist_copy (page_general->priv->orig_attendees);
+               for (link = copy; link; link = g_slist_next (link)) {
+                       link->data = g_strdup (link->data);
+               }
+
+               return copy;
+       }
+
+       new_attendees = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+       attendees = e_meeting_store_get_attendees (page_general->priv->meeting_store);
+
+       for (ii = 0; ii < attendees->len; ii++) {
+               EMeetingAttendee *attendee = g_ptr_array_index (attendees, ii);
+               const gchar *address;
+
+               address = itip_strip_mailto (e_meeting_attendee_get_address (attendee));
+               if (address)
+                       g_hash_table_insert (new_attendees, (gpointer) address, GINT_TO_POINTER (1));
+       }
+
+       for (link = page_general->priv->orig_attendees; link; link = g_slist_next (link)) {
+               const gchar *address = link->data;
+
+               if (address && !g_hash_table_contains (new_attendees, address)) {
+                       removed_attendees = g_slist_prepend (removed_attendees, g_strdup (address));
+               }
+       }
+
+       g_hash_table_destroy (new_attendees);
+
+       return g_slist_reverse (removed_attendees);
+}
diff --git a/calendar/gui/e-comp-editor-page-general.h b/calendar/gui/e-comp-editor-page-general.h
new file mode 100644
index 0000000..fa0f0d4
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-general.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PAGE_GENERAL_H
+#define E_COMP_EDITOR_PAGE_GENERAL_H
+
+#include <libedataserver/libedataserver.h>
+#include <calendar/gui/e-meeting-store.h>
+#include <calendar/gui/e-comp-editor.h>
+#include <calendar/gui/e-comp-editor-page.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PAGE_GENERAL \
+       (e_comp_editor_page_general_get_type ())
+#define E_COMP_EDITOR_PAGE_GENERAL(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_GENERAL, ECompEditorPageGeneral))
+#define E_COMP_EDITOR_PAGE_GENERAL_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_GENERAL, ECompEditorPageGeneralClass))
+#define E_IS_COMP_EDITOR_PAGE_GENERAL(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_GENERAL))
+#define E_IS_COMP_EDITOR_PAGE_GENERAL_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_GENERAL))
+#define E_COMP_EDITOR_PAGE_GENERAL_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_GENERAL, ECompEditorPageGeneralClass))
+
+typedef struct _ECompEditorPageGeneral ECompEditorPageGeneral;
+typedef struct _ECompEditorPageGeneralClass ECompEditorPageGeneralClass;
+typedef struct _ECompEditorPageGeneralPrivate ECompEditorPageGeneralPrivate;
+
+struct _ECompEditorPageGeneral {
+       ECompEditorPage parent;
+
+       ECompEditorPageGeneralPrivate *priv;
+};
+
+struct _ECompEditorPageGeneralClass {
+       ECompEditorPageClass parent_class;
+};
+
+/* ECompEditorPageGeneral uses the first two lines of the grid, counting from zero:
+
+        Organizer: [                       | v ] Source: [                       | v ]
+  [ Attendees... ] +---------------------------------------------------+  [ Add      ]
+                   |                                                   |  [ Edit     ]
+                   |                                                   |  [ Remove   ]
+                   |                                                   |
+                   |                                                   |
+                   +---------------------------------------------------+
+
+ and when show-attendees is set to FALSE, the second line and the organizer part are
+ hidden, making shown only the first line as:
+
+           Source: [                       | v ]
+*/
+
+GType          e_comp_editor_page_general_get_type     (void) G_GNUC_CONST;
+ECompEditorPage *
+               e_comp_editor_page_general_new          (ECompEditor *editor,
+                                                        const gchar *source_label,
+                                                        const gchar *source_extension_name,
+                                                        ESource *select_source,
+                                                        gboolean show_attendees,
+                                                        gint data_column_width);
+const gchar *  e_comp_editor_page_general_get_source_label
+                                                       (ECompEditorPageGeneral *page_general);
+void           e_comp_editor_page_general_set_source_label
+                                                       (ECompEditorPageGeneral *page_general,
+                                                        const gchar *source_label);
+const gchar *  e_comp_editor_page_general_get_source_extension_name
+                                                       (ECompEditorPageGeneral *page_general);
+void           e_comp_editor_page_general_set_source_extension_name
+                                                       (ECompEditorPageGeneral *page_general,
+                                                        const gchar *source_extension_name);
+ESource *      e_comp_editor_page_general_ref_selected_source
+                                                       (ECompEditorPageGeneral *page_general);
+void           e_comp_editor_page_general_set_selected_source
+                                                       (ECompEditorPageGeneral *page_general,
+                                                        ESource *source);
+gboolean       e_comp_editor_page_general_get_show_attendees
+                                                       (ECompEditorPageGeneral *page_general);
+void           e_comp_editor_page_general_set_show_attendees
+                                                       (ECompEditorPageGeneral *page_general,
+                                                        gboolean show_attendees);
+gint           e_comp_editor_page_general_get_data_column_width
+                                                       (ECompEditorPageGeneral *page_general);
+void           e_comp_editor_page_general_set_data_column_width
+                                                       (ECompEditorPageGeneral *page_general,
+                                                        gint data_column_width);
+void           e_comp_editor_page_general_update_view  (ECompEditorPageGeneral *page_general);
+EMeetingStore *        e_comp_editor_page_general_get_meeting_store
+                                                       (ECompEditorPageGeneral *page_general);
+GSList *       e_comp_editor_page_general_get_added_attendees
+                                                       (ECompEditorPageGeneral *page_general);
+GSList *       e_comp_editor_page_general_get_removed_attendees
+                                                       (ECompEditorPageGeneral *page_general);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PAGE_GENERAL_H */
diff --git a/calendar/gui/e-comp-editor-page-recurrence.c b/calendar/gui/e-comp-editor-page-recurrence.c
new file mode 100644
index 0000000..5b933d7
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-recurrence.c
@@ -0,0 +1,2369 @@
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Federico Mena-Quintero <federico ximian com>
+ *     Miguel de Icaza <miguel ximian com>
+ *     Seth Alves <alves hungry com>
+ *     JP Rosevear <jpr ximian com>
+ *     Hans Petter Jansson <hpj ximiman com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "calendar-config.h"
+#include "comp-util.h"
+#include "e-weekday-chooser.h"
+#include "e-date-time-list.h"
+#include "tag-calendar.h"
+
+#include "e-comp-editor-page-recurrence.h"
+
+enum month_num_options {
+       MONTH_NUM_FIRST,
+       MONTH_NUM_SECOND,
+       MONTH_NUM_THIRD,
+       MONTH_NUM_FOURTH,
+       MONTH_NUM_FIFTH,
+       MONTH_NUM_LAST,
+       MONTH_NUM_DAY,
+       MONTH_NUM_OTHER
+};
+
+static const gint month_num_options_map[] = {
+       MONTH_NUM_FIRST,
+       MONTH_NUM_SECOND,
+       MONTH_NUM_THIRD,
+       MONTH_NUM_FOURTH,
+       MONTH_NUM_FIFTH,
+       MONTH_NUM_LAST,
+       MONTH_NUM_DAY,
+       MONTH_NUM_OTHER,
+       -1
+};
+
+enum month_day_options {
+       MONTH_DAY_NTH,
+       MONTH_DAY_MON,
+       MONTH_DAY_TUE,
+       MONTH_DAY_WED,
+       MONTH_DAY_THU,
+       MONTH_DAY_FRI,
+       MONTH_DAY_SAT,
+       MONTH_DAY_SUN
+};
+
+static const gint month_day_options_map[] = {
+       MONTH_DAY_NTH,
+       MONTH_DAY_MON,
+       MONTH_DAY_TUE,
+       MONTH_DAY_WED,
+       MONTH_DAY_THU,
+       MONTH_DAY_FRI,
+       MONTH_DAY_SAT,
+       MONTH_DAY_SUN,
+       -1
+};
+
+enum recur_type {
+       RECUR_NONE,
+       RECUR_SIMPLE,
+       RECUR_CUSTOM
+};
+
+static const gint type_map[] = {
+       RECUR_NONE,
+       RECUR_SIMPLE,
+       RECUR_CUSTOM,
+       -1
+};
+
+static const gint freq_map[] = {
+       ICAL_DAILY_RECURRENCE,
+       ICAL_WEEKLY_RECURRENCE,
+       ICAL_MONTHLY_RECURRENCE,
+       ICAL_YEARLY_RECURRENCE,
+       -1
+};
+
+enum ending_type {
+       ENDING_FOR,
+       ENDING_UNTIL,
+       ENDING_FOREVER
+};
+
+static const gint ending_types_map[] = {
+       ENDING_FOR,
+       ENDING_UNTIL,
+       ENDING_FOREVER,
+       -1
+};
+
+struct _ECompEditorPageRecurrencePrivate {
+       GtkWidget *recr_check_box;
+       GtkWidget *recr_hbox;
+       GtkWidget *recr_interval_value_spin;
+       GtkWidget *recr_interval_unit_combo;
+       GtkWidget *recr_interval_special_box;
+       GtkWidget *recr_ending_combo;
+       GtkWidget *recr_ending_special_box;
+       GtkWidget *recr_cannot_edit_label;
+       GtkWidget *exceptions_tree_view;
+       GtkWidget *exceptions_button_box;
+       GtkWidget *exceptions_add_button;
+       GtkWidget *exceptions_edit_button;
+       GtkWidget *exceptions_remove_button;
+       GtkWidget *preview;
+
+       gboolean is_custom;
+       EDateTimeList *exceptions_store;
+       GCancellable *cancellable;
+
+       /* For weekly recurrences, created by hand */
+       GtkWidget *weekday_chooser;
+       guint8 weekday_day_mask;
+       guint8 weekday_blocked_day_mask;
+
+       /* For monthly recurrences, created by hand */
+       gint month_index;
+
+       GtkWidget *month_day_combo;
+       enum month_day_options month_day;
+
+       GtkWidget *month_num_combo;
+       enum month_num_options month_num;
+
+       /* For ending date, created by hand */
+       GtkWidget *ending_date_edit;
+       struct icaltimetype ending_date_tt;
+
+       /* For ending count of occurrences, created by hand */
+       GtkWidget *ending_count_spin;
+       gint ending_count;
+};
+
+G_DEFINE_TYPE (ECompEditorPageRecurrence, e_comp_editor_page_recurrence, E_TYPE_COMP_EDITOR_PAGE)
+
+static void
+ecep_recurrence_update_preview (ECompEditorPageRecurrence *page_recurrence)
+{
+       ECompEditor *comp_editor;
+       ECalClient *client;
+       ECalComponent *comp;
+       icalcomponent *icalcomp;
+       const icalcomponent *editing_comp;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+       g_return_if_fail (E_IS_CALENDAR (page_recurrence->priv->preview));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_recurrence));
+       client = e_comp_editor_get_source_client (comp_editor);
+       if (!client)
+               client = e_comp_editor_get_target_client (comp_editor);
+
+       e_calendar_item_clear_marks (E_CALENDAR (page_recurrence->priv->preview)->calitem);
+
+       editing_comp = e_comp_editor_get_component (comp_editor);
+       if (!editing_comp || e_cal_util_component_is_instance ((icalcomponent *) editing_comp)) {
+               g_clear_object (&comp_editor);
+               return;
+       }
+
+       icalcomp = icalcomponent_new_clone ((icalcomponent *) editing_comp);
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+       e_comp_editor_fill_component (comp_editor, icalcomp);
+       e_comp_editor_set_updating (comp_editor, FALSE);
+
+       comp = e_cal_component_new_from_icalcomponent (icalcomp);
+
+       if (comp) {
+               icaltimezone *zone = NULL;
+
+               icalcomp = e_cal_component_get_icalcomponent (comp);
+
+               if (icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY)) {
+                       struct icaltimetype dt;
+
+                       dt = icalcomponent_get_dtstart (icalcomp);
+                       zone = (icaltimezone *) dt.zone;
+               }
+
+               e_cal_component_rescan (comp);
+
+               if (!zone)
+                       zone = calendar_config_get_icaltimezone ();
+
+               tag_calendar_by_comp (
+                       E_CALENDAR (page_recurrence->priv->preview), comp,
+                       client, zone, TRUE, FALSE, FALSE, page_recurrence->priv->cancellable);
+               g_object_unref (comp);
+       }
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_recurrence_changed (ECompEditorPageRecurrence *page_recurrence)
+{
+       ECompEditorPage *page;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       page = E_COMP_EDITOR_PAGE (page_recurrence);
+
+       if (e_comp_editor_page_get_updating (page))
+               return;
+
+       e_comp_editor_page_emit_changed (page);
+       ecep_recurrence_update_preview (page_recurrence);
+}
+
+static void
+ecep_recurrence_append_exception (ECompEditorPageRecurrence *page_recurrence,
+                                 const struct icaltimetype itt)
+{
+       GtkTreeView *view;
+       GtkTreeIter  iter;
+
+       view = GTK_TREE_VIEW (page_recurrence->priv->exceptions_tree_view);
+
+       e_date_time_list_append (page_recurrence->priv->exceptions_store, &iter, itt);
+       gtk_tree_selection_select_iter (gtk_tree_view_get_selection (view), &iter);
+}
+
+static void
+ecep_recurrence_fill_exception_widgets (ECompEditorPageRecurrence *page_recurrence,
+                                       icalcomponent *component)
+{
+       icalproperty *prop;
+
+       e_date_time_list_clear (page_recurrence->priv->exceptions_store);
+
+       for (prop = icalcomponent_get_first_property (component, ICAL_EXDATE_PROPERTY);
+            prop;
+            prop = icalcomponent_get_next_property (component, ICAL_EXDATE_PROPERTY)) {
+               struct icaltimetype itt;
+
+               itt = icalproperty_get_exdate (prop);
+               if (!icaltime_is_valid_time (itt) ||
+                   icaltime_is_null_time (itt))
+                       continue;
+
+               ecep_recurrence_append_exception (page_recurrence, itt);
+       }
+}
+
+static void
+ecep_recurrence_exceptions_selection_changed_cb (GtkTreeSelection *selection,
+                                                ECompEditorPageRecurrence *page_recurrence)
+{
+       gboolean any_selected;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       any_selected = gtk_tree_selection_count_selected_rows (selection) > 0;
+
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_edit_button, any_selected);
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_remove_button, any_selected);
+}
+
+static GtkWidget *
+ecep_recurrence_create_exception_dialog (ECompEditorPageRecurrence *page_recurrence,
+                                        const gchar *title,
+                                        GtkWidget **out_date_edit)
+{
+       GtkWidget *dialog, *toplevel;
+       GtkWidget *container;
+       EDateEdit *date_edit;
+
+       toplevel = gtk_widget_get_toplevel (page_recurrence->priv->recr_check_box);
+
+       if (!GTK_IS_WINDOW (toplevel))
+               toplevel = NULL;
+
+       dialog = gtk_dialog_new_with_buttons (
+               title, GTK_WINDOW (toplevel),
+               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+               _("_Cancel"), GTK_RESPONSE_REJECT,
+               _("_OK"), GTK_RESPONSE_ACCEPT,
+               NULL);
+
+       *out_date_edit = e_date_edit_new ();
+       date_edit = E_DATE_EDIT (*out_date_edit);
+       e_date_edit_set_show_date (date_edit, TRUE);
+       e_date_edit_set_show_time (date_edit, FALSE);
+
+       gtk_widget_show (*out_date_edit);
+       container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+       gtk_box_pack_start (GTK_BOX (container), *out_date_edit, FALSE, TRUE, 6);
+
+       return dialog;
+}
+
+static void
+ecep_recurrence_exceptions_add_clicked_cb (GtkButton *button,
+                                          ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkWidget *dialog, *date_edit;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       dialog = ecep_recurrence_create_exception_dialog (page_recurrence, _("Add exception"), &date_edit);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               struct icaltimetype itt = icaltime_null_time ();
+
+               /* We use DATE values for exceptions, so we don't need a TZID. */
+               itt.zone = NULL;
+               itt.hour = 0;
+               itt.minute = 0;
+               itt.second = 0;
+               itt.is_date = 1;
+
+               if (e_date_edit_get_date (E_DATE_EDIT (date_edit), &itt.year, &itt.month, &itt.day)) {
+                       ecep_recurrence_append_exception (page_recurrence, itt);
+                       ecep_recurrence_changed (page_recurrence);
+               }
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+ecep_recurrence_exceptions_edit_clicked_cb (GtkButton *button,
+                                           ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkWidget *dialog, *date_edit;
+       const struct icaltimetype *current_itt;
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_recurrence->priv->exceptions_tree_view));
+       g_return_if_fail (gtk_tree_selection_get_selected (selection, NULL, &iter));
+
+       current_itt = e_date_time_list_get_date_time (page_recurrence->priv->exceptions_store, &iter);
+       g_return_if_fail (current_itt != NULL);
+
+       dialog = ecep_recurrence_create_exception_dialog (page_recurrence, _("Modify exception"), &date_edit);
+       e_date_edit_set_date (E_DATE_EDIT (date_edit),
+               current_itt->year, current_itt->month, current_itt->day);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               struct icaltimetype itt = icaltime_null_time ();
+
+               /* We use DATE values for exceptions, so we don't need a TZID. */
+               itt.zone = NULL;
+               itt.hour = 0;
+               itt.minute = 0;
+               itt.second = 0;
+               itt.is_date = 1;
+
+               if (e_date_edit_get_date (E_DATE_EDIT (date_edit), &itt.year, &itt.month, &itt.day)) {
+                       e_date_time_list_set_date_time (page_recurrence->priv->exceptions_store, &iter, itt);
+                       ecep_recurrence_changed (page_recurrence);
+               }
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+ecep_recurrence_exceptions_remove_clicked_cb (GtkButton *button,
+                                             ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+       GtkTreePath *path;
+       gboolean valid_iter;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_recurrence->priv->exceptions_tree_view));
+       g_return_if_fail (gtk_tree_selection_get_selected (selection, NULL, &iter));
+
+       path = gtk_tree_model_get_path (GTK_TREE_MODEL (page_recurrence->priv->exceptions_store), &iter);
+       e_date_time_list_remove (page_recurrence->priv->exceptions_store, &iter);
+
+       /* Select closest item after removal */
+       valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL (page_recurrence->priv->exceptions_store), 
&iter, path);
+       if (!valid_iter) {
+               gtk_tree_path_prev (path);
+               valid_iter = gtk_tree_model_get_iter (GTK_TREE_MODEL 
(page_recurrence->priv->exceptions_store), &iter, path);
+       }
+
+       if (valid_iter)
+               gtk_tree_selection_select_iter (selection, &iter);
+
+       gtk_tree_path_free (path);
+
+       ecep_recurrence_changed (page_recurrence);
+}
+
+static void
+ecep_recurrence_checkbox_toggled_cb (GtkToggleButton *checkbox,
+                                    ECompEditorPageRecurrence *page_recurrence)
+{
+       ECompEditor *comp_editor;
+       ECompEditorPage *page;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       page = E_COMP_EDITOR_PAGE (page_recurrence);
+       comp_editor = e_comp_editor_page_ref_editor (page);
+       e_comp_editor_sensitize_widgets (comp_editor);
+       g_clear_object (&comp_editor);
+
+       e_comp_editor_page_emit_changed (page);
+}
+
+static struct tm
+ecep_recurrence_get_current_time_cb (ECalendarItem *calitem,
+                                    gpointer user_data)
+{
+       struct icaltimetype today;
+
+       today = icaltime_today ();
+
+       return icaltimetype_to_tm (&today);
+}
+
+static GtkWidget *
+ecep_recurrence_get_box_first_child (GtkWidget *box)
+{
+       GtkWidget *first_child;
+       GList *children;
+
+       if (!box)
+               return NULL;
+
+       g_return_val_if_fail (GTK_IS_BOX (box), NULL);
+
+       children = gtk_container_get_children (GTK_CONTAINER (box));
+       if (!children)
+               return NULL;
+
+       first_child = children->data;
+
+       g_list_free (children);
+
+       return first_child;
+}
+
+/* Creates the special contents for weekly recurrences */
+static void
+ecep_recurrence_make_weekly_special (ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkWidget *hbox;
+       GtkWidget *label;
+       EWeekdayChooser *chooser;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       g_return_if_fail (ecep_recurrence_get_box_first_child 
(page_recurrence->priv->recr_interval_special_box) == NULL);
+       g_return_if_fail (page_recurrence->priv->weekday_chooser == NULL);
+
+       /* Create the widgets */
+
+       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+       gtk_container_add (GTK_CONTAINER (page_recurrence->priv->recr_interval_special_box), hbox);
+
+       /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] week(s) on 
[Wednesday] [forever]'
+        * (dropdown menu options are in [square brackets]). This means that after the 'on', name of a week 
day always follows. */
+       label = gtk_label_new (_("on"));
+       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
+
+       page_recurrence->priv->weekday_chooser = e_weekday_chooser_new ();
+       chooser = E_WEEKDAY_CHOOSER (page_recurrence->priv->weekday_chooser);
+
+       gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (chooser), FALSE, FALSE, 6);
+
+       gtk_widget_show_all (hbox);
+
+       /* Set the weekdays */
+
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_SUNDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 0)) != 0);
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_MONDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 1)) != 0);
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_TUESDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 2)) != 0);
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_WEDNESDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 3)) != 0);
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_THURSDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 4)) != 0);
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_FRIDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 5)) != 0);
+       e_weekday_chooser_set_selected (
+               chooser, G_DATE_SATURDAY,
+               (page_recurrence->priv->weekday_day_mask & (1 << 6)) != 0);
+
+       g_signal_connect_swapped (
+               chooser, "changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+}
+
+/* Creates the subtree for the monthly recurrence number */
+static void
+ecep_recurrence_make_recur_month_num_subtree (GtkTreeStore *store,
+                                             GtkTreeIter *par,
+                                             const gchar *title,
+                                             gint start,
+                                             gint end)
+{
+       GtkTreeIter iter, parent;
+       gint i;
+
+       gtk_tree_store_append (store, &parent, par);
+       gtk_tree_store_set (store, &parent, 0, _(title), 1, -1, -1);
+
+       for (i = start; i < end; i++) {
+               gtk_tree_store_append (store, &iter, &parent);
+               gtk_tree_store_set (store, &iter, 0, _(e_cal_recur_nth[i]), 1, i + 1, -1);
+       }
+}
+
+static void
+ecep_recurrence_only_leaf_sensitive (GtkCellLayout *cell_layout,
+                                    GtkCellRenderer *cell,
+                                    GtkTreeModel *tree_model,
+                                    GtkTreeIter *iter,
+                                    gpointer data)
+{
+       gboolean sensitive;
+
+       sensitive = !gtk_tree_model_iter_has_child (tree_model, iter);
+
+       g_object_set (cell, "sensitive", sensitive, NULL);
+}
+
+static GtkWidget *
+ecep_recurrence_make_recur_month_num_combo (gint month_index)
+{
+       static const gchar *options[] = {
+               /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] month(s) on 
the [first] [Monday] [forever]'
+                * (dropdown menu options are in [square brackets]). This means that after 'first', either 
the string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow.
+                */
+               NC_("ECompEditorPageRecur", "first"),
+               /* TRANSLATORS: here, "second" is the ordinal number (like "third"), not the time division 
(like "minute")
+                * Entire string is for example: This appointment recurs/Every [x] month(s) on the [second] 
[Monday] [forever]'
+                * (dropdown menu options are in [square brackets]). This means that after 'second', either 
the string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow.
+                */
+               NC_("ECompEditorPageRecur", "second"),
+               /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] month(s) on 
the [third] [Monday] [forever]'
+                * (dropdown menu options are in [square brackets]). This means that after 'third', either 
the string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow.
+                */
+               NC_("ECompEditorPageRecur", "third"),
+               /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] month(s) on 
the [fourth] [Monday] [forever]'
+                * (dropdown menu options are in [square brackets]). This means that after 'fourth', either 
the string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow.
+                */
+               NC_("ECompEditorPageRecur", "fourth"),
+               /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] month(s) on 
the [fifth] [Monday] [forever]'
+                * (dropdown menu options are in [square brackets]). This means that after 'fifth', either 
the string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow.
+                */
+               NC_("ECompEditorPageRecur", "fifth"),
+               /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] month(s) on 
the [last] [Monday] [forever]'
+                * (dropdown menu options are in [square brackets]). This means that after 'last', either the 
string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow.
+                */
+               NC_("ECompEditorPageRecur", "last")
+       };
+
+       gint i;
+       GtkTreeStore *store;
+       GtkTreeIter iter;
+       GtkWidget *combo;
+       GtkCellRenderer *cell;
+
+       store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT);
+
+       /* Relation */
+       for (i = 0; i < G_N_ELEMENTS (options); i++) {
+               gtk_tree_store_append (store, &iter, NULL);
+               gtk_tree_store_set (store, &iter,
+                       0, g_dpgettext2 (GETTEXT_PACKAGE, "ECompEditorPageRecur", options[i]),
+                       1, month_num_options_map[i],
+                       -1);
+       }
+
+       /* Current date */
+       gtk_tree_store_append (store, &iter, NULL);
+       gtk_tree_store_set (store, &iter, 0, _(e_cal_recur_nth[month_index - 1]), 1, MONTH_NUM_DAY, -1);
+
+       gtk_tree_store_append (store, &iter, NULL);
+       /* TRANSLATORS: Entire string is for example: This appointment recurs/Every [x] month(s) on the 
[Other date] [11th to 20th] [17th] [forever]'
+        * (dropdown menu options are in [square brackets]). */
+       gtk_tree_store_set (store, &iter, 0, C_("ECompEditorPageRecur", "Other Date"), 1, MONTH_NUM_OTHER, 
-1);
+
+       /* TRANSLATORS: This is a submenu option string to split the date range into three submenus to choose 
the exact day of
+        * the month to setup an appointment recurrence. The entire string is for example: This appointment 
recurs/Every [x] month(s)
+        * on the [Other date] [1st to 10th] [7th] [forever]' (dropdown menu options are in [square 
brackets]).
+        */
+       ecep_recurrence_make_recur_month_num_subtree (store, &iter, C_("ECompEditorPageRecur", "1st to 
10th"), 0, 10);
+
+       /* TRANSLATORS: This is a submenu option string to split the date range into three submenus to choose 
the exact day of
+        * the month to setup an appointment recurrence. The entire string is for example: This appointment 
recurs/Every [x] month(s)
+        * on the [Other date] [11th to 20th] [17th] [forever]' (dropdown menu options are in [square 
brackets]).
+        */
+       ecep_recurrence_make_recur_month_num_subtree (store, &iter, C_("ECompEditorPageRecur", "11th to 
20th"), 10, 20);
+
+       /* TRANSLATORS: This is a submenu option string to split the date range into three submenus to choose 
the exact day of
+        * the month to setup an appointment recurrence. The entire string is for example: This appointment 
recurs/Every [x] month(s)
+        * on the [Other date] [21th to 31th] [27th] [forever]' (dropdown menu options are in [square 
brackets]).
+        */
+       ecep_recurrence_make_recur_month_num_subtree (store, &iter, C_("ECompEditorPageRecur", "21st to 
31st"), 20, 31);
+
+       combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+       g_object_unref (store);
+
+       cell = gtk_cell_renderer_text_new ();
+       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell, "text", 0, NULL);
+
+       gtk_cell_layout_set_cell_data_func (
+               GTK_CELL_LAYOUT (combo),
+               cell,
+               ecep_recurrence_only_leaf_sensitive,
+               NULL, NULL);
+
+       return combo;
+}
+
+/* Creates the combo box for the monthly recurrence days */
+static GtkWidget *
+ecep_recurrence_make_recur_month_combobox (void)
+{
+       static const gchar *options[] = {
+               /* For Translator : 'day' is part of the sentence of the form 'appointment recurs/Every [x] 
month(s) on the [first] [day] [forever]'
+                * (dropdown menu options are in[square brackets]). This means that after 'first', either the 
string 'day' or
+                * the name of a week day (like 'Monday' or 'Friday') always follow. */
+               NC_("ECompEditorPageRecur", "day"),
+               NC_("ECompEditorPageRecur", "Monday"),
+               NC_("ECompEditorPageRecur", "Tuesday"),
+               NC_("ECompEditorPageRecur", "Wednesday"),
+               NC_("ECompEditorPageRecur", "Thursday"),
+               NC_("ECompEditorPageRecur", "Friday"),
+               NC_("ECompEditorPageRecur", "Saturday"),
+               NC_("ECompEditorPageRecur", "Sunday")
+       };
+
+       GtkWidget *combo;
+       gint i;
+
+       combo = gtk_combo_box_text_new ();
+
+       for (i = 0; i < G_N_ELEMENTS (options); i++) {
+               gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo),
+                       g_dpgettext2 (GETTEXT_PACKAGE, "ECompEditorPageRecur", options[i]));
+       }
+
+       return combo;
+}
+
+static void
+ecep_recurrence_month_num_combo_changed_cb (GtkComboBox *combo,
+                                           ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkTreeIter iter;
+       enum month_num_options month_num;
+       enum month_day_options month_day;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       month_day = e_dialog_combo_box_get (
+               page_recurrence->priv->month_day_combo,
+               month_day_options_map);
+
+       if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (page_recurrence->priv->month_num_combo), &iter)) {
+               gint value;
+               GtkTreeIter parent;
+               GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX 
(page_recurrence->priv->month_num_combo));
+
+               gtk_tree_model_get (model, &iter, 1, &value, -1);
+
+               if (value == -1) {
+                       return;
+               }
+
+               if (gtk_tree_model_iter_parent (model, &parent, &iter)) {
+                       /* it's a leaf, thus the day number */
+                       month_num = MONTH_NUM_DAY;
+                       page_recurrence->priv->month_index = value;
+
+                       g_return_if_fail (gtk_tree_model_iter_nth_child (model, &iter, NULL, month_num));
+
+                       gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 0, 
_(e_cal_recur_nth[page_recurrence->priv->month_index - 1]), -1);
+                       gtk_combo_box_set_active_iter (GTK_COMBO_BOX 
(page_recurrence->priv->month_num_combo), &iter);
+               } else {
+                       /* top level node */
+                       month_num = value;
+
+                       if (month_num == MONTH_NUM_OTHER)
+                               month_num = MONTH_NUM_DAY;
+               }
+       } else {
+               month_num = 0;
+       }
+
+       if (month_num == MONTH_NUM_DAY && month_day != MONTH_DAY_NTH)
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->month_day_combo,
+                       MONTH_DAY_NTH,
+                       month_day_options_map);
+       else if (month_num != MONTH_NUM_DAY && month_num != MONTH_NUM_LAST && month_day == MONTH_DAY_NTH)
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->month_day_combo,
+                       MONTH_DAY_MON,
+                       month_num_options_map);
+
+       ecep_recurrence_changed (page_recurrence);
+}
+
+/* Callback used when the monthly day selection changes.  We need
+ * to change the valid range of the day index spin button; e.g. days
+ * are 1-31 while a Sunday is the 1st through 5th.
+ */
+static void
+ecep_recurrence_month_day_combo_changed_cb (GtkComboBox *combo,
+                                           ECompEditorPageRecurrence *page_recurrence)
+{
+       enum month_num_options month_num;
+       enum month_day_options month_day;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       month_num = e_dialog_combo_box_get (
+               page_recurrence->priv->month_num_combo,
+               month_num_options_map);
+       month_day = e_dialog_combo_box_get (
+               page_recurrence->priv->month_day_combo,
+               month_day_options_map);
+       if (month_day == MONTH_DAY_NTH && month_num != MONTH_NUM_LAST && month_num != MONTH_NUM_DAY)
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->month_num_combo,
+                       MONTH_NUM_DAY,
+                       month_num_options_map);
+       else if (month_day != MONTH_DAY_NTH && month_num == MONTH_NUM_DAY)
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->month_num_combo,
+                       MONTH_NUM_FIRST,
+                       month_num_options_map);
+
+       ecep_recurrence_changed (page_recurrence);
+}
+
+/* Creates the special contents for monthly recurrences */
+static void
+ecep_recurrence_make_monthly_special (ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkWidget *hbox;
+       GtkWidget *label;
+       GtkAdjustment *adj;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       g_return_if_fail (ecep_recurrence_get_box_first_child 
(page_recurrence->priv->recr_interval_special_box) == NULL);
+       g_return_if_fail (page_recurrence->priv->month_day_combo == NULL);
+
+       /* Create the widgets */
+
+       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+       gtk_container_add (GTK_CONTAINER (page_recurrence->priv->recr_interval_special_box), hbox);
+
+       /* TRANSLATORS: Entire string is for example: 'This appointment recurs/Every [x] month(s) on the 
[second] [Tuesday] [forever]'
+        * (dropdown menu options are in [square brackets])."
+        */
+       label = gtk_label_new (C_("ECompEditorPageRecur", "on the"));
+       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
+
+       adj = GTK_ADJUSTMENT (gtk_adjustment_new (1, 1, 31, 1, 10, 10));
+
+       page_recurrence->priv->month_num_combo = ecep_recurrence_make_recur_month_num_combo 
(page_recurrence->priv->month_index);
+       gtk_box_pack_start (
+               GTK_BOX (hbox), page_recurrence->priv->month_num_combo,
+               FALSE, FALSE, 6);
+
+       page_recurrence->priv->month_day_combo = ecep_recurrence_make_recur_month_combobox ();
+       gtk_box_pack_start (
+               GTK_BOX (hbox), page_recurrence->priv->month_day_combo,
+               FALSE, FALSE, 6);
+
+       gtk_widget_show_all (hbox);
+
+       /* Set the options */
+       e_dialog_combo_box_set (
+               page_recurrence->priv->month_num_combo,
+               page_recurrence->priv->month_num,
+               month_num_options_map);
+       e_dialog_combo_box_set (
+               page_recurrence->priv->month_day_combo,
+               page_recurrence->priv->month_day,
+               month_day_options_map);
+
+       g_signal_connect_swapped (
+               adj, "value-changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+
+       g_signal_connect (
+               page_recurrence->priv->month_num_combo, "changed",
+               G_CALLBACK (ecep_recurrence_month_num_combo_changed_cb), page_recurrence);
+
+       g_signal_connect (
+               page_recurrence->priv->month_day_combo, "changed",
+               G_CALLBACK (ecep_recurrence_month_day_combo_changed_cb), page_recurrence);
+}
+
+/* Changes the recurrence-special widget to match the interval units.
+ *
+ * For daily recurrences: nothing.
+ * For weekly recurrences: weekday selector.
+ * For monthly recurrences: "on the" <nth> [day, Weekday]
+ * For yearly recurrences: nothing.
+ */
+static void
+ecep_recurrence_make_recurrence_special (ECompEditorPageRecurrence *page_recurrence)
+{
+       icalrecurrencetype_frequency frequency;
+       GtkWidget *child;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       if (page_recurrence->priv->month_num_combo != NULL) {
+               gtk_widget_destroy (page_recurrence->priv->month_num_combo);
+               page_recurrence->priv->month_num_combo = NULL;
+       }
+
+       child = ecep_recurrence_get_box_first_child (page_recurrence->priv->recr_interval_special_box);
+       if (child != NULL) {
+               gtk_widget_destroy (child);
+
+               page_recurrence->priv->weekday_chooser = NULL;
+               page_recurrence->priv->month_day_combo = NULL;
+       }
+
+       frequency = e_dialog_combo_box_get (page_recurrence->priv->recr_interval_unit_combo, freq_map);
+
+       switch (frequency) {
+       case ICAL_DAILY_RECURRENCE:
+               gtk_widget_hide (page_recurrence->priv->recr_interval_special_box);
+               break;
+
+       case ICAL_WEEKLY_RECURRENCE:
+               ecep_recurrence_make_weekly_special (page_recurrence);
+               gtk_widget_show (page_recurrence->priv->recr_interval_special_box);
+               break;
+
+       case ICAL_MONTHLY_RECURRENCE:
+               ecep_recurrence_make_monthly_special (page_recurrence);
+               gtk_widget_show (page_recurrence->priv->recr_interval_special_box);
+               break;
+
+       case ICAL_YEARLY_RECURRENCE:
+               gtk_widget_hide (page_recurrence->priv->recr_interval_special_box);
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+}
+
+/* Counts the elements in the by_xxx fields of an icalrecurrencetype */
+static gint
+ecep_recurrence_count_by_xxx (gshort *field,
+                             gint max_elements)
+{
+       gint ii;
+
+       for (ii = 0; ii < max_elements; ii++)
+               if (field[ii] == ICAL_RECURRENCE_ARRAY_MAX)
+                       break;
+
+       return ii;
+}
+
+/* Creates the special contents for "ending until" (end date) recurrences */
+static void
+ecep_recurrence_make_ending_until_special (ECompEditorPageRecurrence *page_recurrence)
+{
+       ECompEditor *comp_editor;
+       guint32 flags;
+       const icalcomponent *icomp;
+       EDateEdit *date_edit;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+       g_return_if_fail (ecep_recurrence_get_box_first_child 
(page_recurrence->priv->recr_ending_special_box) == NULL);
+       g_return_if_fail (page_recurrence->priv->ending_date_edit == NULL);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_recurrence));
+       flags = e_comp_editor_get_flags (comp_editor);
+
+       /* Create the widget */
+
+       page_recurrence->priv->ending_date_edit = e_date_edit_new ();
+       date_edit = E_DATE_EDIT (page_recurrence->priv->ending_date_edit);
+       e_date_edit_set_show_date (date_edit, TRUE);
+       e_date_edit_set_show_time (date_edit, FALSE);
+
+       gtk_container_add (
+               GTK_CONTAINER (page_recurrence->priv->recr_ending_special_box),
+               page_recurrence->priv->ending_date_edit);
+       gtk_widget_show (page_recurrence->priv->ending_date_edit);
+
+       /* Set the value */
+
+       icomp = e_comp_editor_get_component (comp_editor);
+       if ((flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0 && icomp) {
+               struct icaltimetype itt;
+
+               itt = icalcomponent_get_dtstart ((icalcomponent *) icomp);
+               /* Setting the default until time to 2 weeks */
+               icaltime_adjust (&itt, 14, 0, 0, 0);
+
+               e_date_edit_set_date (date_edit, itt.year, itt.month, itt.day);
+       } else {
+               e_date_edit_set_date (date_edit,
+                       page_recurrence->priv->ending_date_tt.year,
+                       page_recurrence->priv->ending_date_tt.month,
+                       page_recurrence->priv->ending_date_tt.day);
+       }
+
+       g_signal_connect_swapped (
+               date_edit, "changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+
+       e_date_edit_set_get_time_callback (date_edit,
+               (EDateEditGetTimeCallback) ecep_recurrence_get_current_time_cb,
+               NULL, NULL);
+
+       g_clear_object (&comp_editor);
+}
+
+/* Creates the special contents for the occurrence count case */
+static void
+ecep_recurrence_make_ending_count_special (ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkWidget *hbox;
+       GtkWidget *label;
+       GtkAdjustment *adj;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       g_return_if_fail (ecep_recurrence_get_box_first_child 
(page_recurrence->priv->recr_ending_special_box) == NULL);
+       g_return_if_fail (page_recurrence->priv->ending_count_spin == NULL);
+
+       /* Create the widgets */
+
+       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+       gtk_container_add (GTK_CONTAINER (page_recurrence->priv->recr_ending_special_box), hbox);
+
+       adj = GTK_ADJUSTMENT (gtk_adjustment_new (1, 1, 10000, 1, 10, 0));
+       page_recurrence->priv->ending_count_spin = gtk_spin_button_new (adj, 1, 0);
+       gtk_spin_button_set_numeric ((GtkSpinButton *) page_recurrence->priv->ending_count_spin, TRUE);
+       gtk_box_pack_start (
+               GTK_BOX (hbox), page_recurrence->priv->ending_count_spin,
+               FALSE, FALSE, 6);
+
+       label = gtk_label_new (C_("ECompEditorPageRecur", "occurrences"));
+       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
+
+       gtk_widget_show_all (hbox);
+
+       /* Set the values */
+
+       gtk_spin_button_set_value (
+               GTK_SPIN_BUTTON (page_recurrence->priv->ending_count_spin),
+               page_recurrence->priv->ending_count);
+
+       g_signal_connect_swapped (
+               adj, "value-changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+}
+
+/* Changes the recurrence-ending-special widget to match the ending date option
+ *
+ * For: <n> [days, weeks, months, years, occurrences]
+ * Until: <date selector>
+ * Forever: nothing.
+ */
+static void
+ecep_recurrence_make_ending_special (ECompEditorPageRecurrence *page_recurrence)
+{
+       enum ending_type ending_type;
+       GtkWidget *child;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       child = ecep_recurrence_get_box_first_child (page_recurrence->priv->recr_ending_special_box);
+       if (child != NULL) {
+               gtk_widget_destroy (child);
+
+               page_recurrence->priv->ending_date_edit = NULL;
+               page_recurrence->priv->ending_count_spin = NULL;
+       }
+
+       ending_type = e_dialog_combo_box_get (page_recurrence->priv->recr_ending_combo, ending_types_map);
+
+       switch (ending_type) {
+       case ENDING_FOR:
+               ecep_recurrence_make_ending_count_special (page_recurrence);
+               gtk_widget_show (page_recurrence->priv->recr_ending_special_box);
+               break;
+
+       case ENDING_UNTIL:
+               ecep_recurrence_make_ending_until_special (page_recurrence);
+               gtk_widget_show (page_recurrence->priv->recr_ending_special_box);
+               break;
+
+       case ENDING_FOREVER:
+               gtk_widget_hide (page_recurrence->priv->recr_ending_special_box);
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+}
+
+/* Fills the recurrence ending date widgets with the values from the calendar
+ * component.
+ */
+static void
+ecep_recurrence_fill_ending_date (ECompEditorPageRecurrence *page_recurrence,
+                                 struct icalrecurrencetype *rrule,
+                                 icalcomponent *component)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       g_signal_handlers_block_matched (page_recurrence->priv->recr_ending_combo, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+
+       if (rrule->count == 0) {
+               if (rrule->until.year == 0) {
+                       /* Forever */
+
+                       e_dialog_combo_box_set (
+                               page_recurrence->priv->recr_ending_combo,
+                               ENDING_FOREVER,
+                               ending_types_map);
+               } else {
+                       /* Ending date */
+
+                       if (!rrule->until.is_date) {
+                               icaltimezone *from_zone, *to_zone;
+                               struct icaltimetype dtstart;
+
+                               dtstart = icalcomponent_get_dtstart (component);
+
+                               from_zone = icaltimezone_get_utc_timezone ();
+                               to_zone = (icaltimezone *) dtstart.zone;
+
+                               if (to_zone)
+                                       icaltimezone_convert_time (&rrule->until, from_zone, to_zone);
+
+                               rrule->until.hour = 0;
+                               rrule->until.minute = 0;
+                               rrule->until.second = 0;
+                               rrule->until.is_date = TRUE;
+                               rrule->until.is_utc = FALSE;
+                       }
+
+                       page_recurrence->priv->ending_date_tt = rrule->until;
+                       e_dialog_combo_box_set (
+                               page_recurrence->priv->recr_ending_combo,
+                               ENDING_UNTIL,
+                               ending_types_map);
+               }
+       } else {
+               /* Count of occurrences */
+
+               page_recurrence->priv->ending_count = rrule->count;
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->recr_ending_combo,
+                       ENDING_FOR,
+                       ending_types_map);
+       }
+
+       g_signal_handlers_unblock_matched (page_recurrence->priv->recr_ending_combo, G_SIGNAL_MATCH_DATA, 0, 
0, NULL, NULL, page_recurrence);
+
+       ecep_recurrence_make_ending_special (page_recurrence);
+}
+
+/* Computes a weekday mask for the start day of a calendar component,
+ * for use in a WeekdayPicker widget.
+ */
+static guint8
+ecep_recurrence_get_start_weekday_mask (icalcomponent *component)
+{
+       struct icaltimetype dtstart;
+       guint8 retval;
+
+       if (!component)
+               return 0;
+
+       dtstart = icalcomponent_get_dtstart (component);
+
+       if (icaltime_is_valid_time (dtstart)) {
+               gshort weekday;
+
+               weekday = icaltime_day_of_week (dtstart);
+               retval = 0x1 << (weekday - 1);
+       } else
+               retval = 0;
+
+       return retval;
+}
+
+/* Sets some sane defaults for the data sources for the recurrence special
+ * widgets, even if they will not be used immediately.
+ */
+static void
+ecep_recurrence_set_special_defaults (ECompEditorPageRecurrence *page_recurrence,
+                                     icalcomponent *component)
+{
+       guint8 mask;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       mask = ecep_recurrence_get_start_weekday_mask (component);
+
+       page_recurrence->priv->weekday_day_mask = mask;
+       page_recurrence->priv->weekday_blocked_day_mask = mask;
+}
+
+static void
+ecep_recurrence_clear_widgets (ECompEditorPageRecurrence *page_recurrence)
+{
+       GtkAdjustment *adj;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       page_recurrence->priv->is_custom = FALSE;
+
+       page_recurrence->priv->weekday_day_mask = 0;
+
+       page_recurrence->priv->month_index = 1;
+       page_recurrence->priv->month_num = MONTH_NUM_DAY;
+       page_recurrence->priv->month_day = MONTH_DAY_NTH;
+
+       g_signal_handlers_block_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_recurrence->priv->recr_check_box), FALSE);
+       g_signal_handlers_unblock_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+
+       adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON 
(page_recurrence->priv->recr_interval_value_spin));
+       g_signal_handlers_block_matched (adj, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_recurrence->priv->recr_interval_value_spin), 1);
+       g_signal_handlers_unblock_matched (adj, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+
+       g_signal_handlers_block_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+       e_dialog_combo_box_set (page_recurrence->priv->recr_interval_unit_combo, ICAL_DAILY_RECURRENCE, 
freq_map);
+       g_signal_handlers_unblock_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+
+       page_recurrence->priv->ending_date_tt = icaltime_today ();
+       page_recurrence->priv->ending_count = 2;
+
+       g_signal_handlers_block_matched (page_recurrence->priv->recr_ending_combo, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+       e_dialog_combo_box_set (page_recurrence->priv->recr_ending_combo,
+               page_recurrence->priv->ending_count == -1 ? ENDING_FOREVER : ENDING_FOR,
+               ending_types_map);
+       g_signal_handlers_unblock_matched (page_recurrence->priv->recr_ending_combo, G_SIGNAL_MATCH_DATA, 0, 
0, NULL, NULL, page_recurrence);
+       if (page_recurrence->priv->ending_count == -1)
+               page_recurrence->priv->ending_count = 2;
+       ecep_recurrence_make_ending_special (page_recurrence);
+
+       /* Exceptions list */
+       e_date_time_list_clear (page_recurrence->priv->exceptions_store);
+}
+
+static void
+ecep_recurrence_simple_recur_to_comp (ECompEditorPageRecurrence *page_recurrence,
+                                     icalcomponent *component)
+{
+       struct icalrecurrencetype r;
+       enum ending_type ending_type;
+       icalproperty *prop;
+       gboolean date_set;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       icalrecurrencetype_clear (&r);
+
+       /* Frequency, interval, week start */
+
+       r.freq = e_dialog_combo_box_get (page_recurrence->priv->recr_interval_unit_combo, freq_map);
+       r.interval = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(page_recurrence->priv->recr_interval_value_spin));
+
+       switch (calendar_config_get_week_start_day ()) {
+               case G_DATE_MONDAY:
+                       r.week_start = ICAL_MONDAY_WEEKDAY;
+                       break;
+               case G_DATE_TUESDAY:
+                       r.week_start = ICAL_TUESDAY_WEEKDAY;
+                       break;
+               case G_DATE_WEDNESDAY:
+                       r.week_start = ICAL_WEDNESDAY_WEEKDAY;
+                       break;
+               case G_DATE_THURSDAY:
+                       r.week_start = ICAL_THURSDAY_WEEKDAY;
+                       break;
+               case G_DATE_FRIDAY:
+                       r.week_start = ICAL_FRIDAY_WEEKDAY;
+                       break;
+               case G_DATE_SATURDAY:
+                       r.week_start = ICAL_SATURDAY_WEEKDAY;
+                       break;
+               case G_DATE_SUNDAY:
+                       r.week_start = ICAL_SUNDAY_WEEKDAY;
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       break;
+       }
+
+       /* Frequency-specific data */
+
+       switch (r.freq) {
+       case ICAL_DAILY_RECURRENCE:
+               /* Nothing else is required */
+               break;
+
+       case ICAL_WEEKLY_RECURRENCE: {
+               EWeekdayChooser *chooser;
+               gint ii;
+
+               g_return_if_fail (ecep_recurrence_get_box_first_child 
(page_recurrence->priv->recr_interval_special_box) != NULL);
+               g_return_if_fail (E_IS_WEEKDAY_CHOOSER (page_recurrence->priv->weekday_chooser));
+
+               chooser = E_WEEKDAY_CHOOSER (page_recurrence->priv->weekday_chooser);
+
+               ii = 0;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_SUNDAY))
+                       r.by_day[ii++] = ICAL_SUNDAY_WEEKDAY;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_MONDAY))
+                       r.by_day[ii++] = ICAL_MONDAY_WEEKDAY;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_TUESDAY))
+                       r.by_day[ii++] = ICAL_TUESDAY_WEEKDAY;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_WEDNESDAY))
+                       r.by_day[ii++] = ICAL_WEDNESDAY_WEEKDAY;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_THURSDAY))
+                       r.by_day[ii++] = ICAL_THURSDAY_WEEKDAY;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_FRIDAY))
+                       r.by_day[ii++] = ICAL_FRIDAY_WEEKDAY;
+
+               if (e_weekday_chooser_get_selected (chooser, E_DATE_SATURDAY))
+                       r.by_day[ii] = ICAL_SATURDAY_WEEKDAY;
+
+               break;
+       }
+
+       case ICAL_MONTHLY_RECURRENCE: {
+               enum month_num_options month_num;
+               enum month_day_options month_day;
+
+               g_return_if_fail (ecep_recurrence_get_box_first_child 
(page_recurrence->priv->recr_interval_special_box) != NULL);
+               g_return_if_fail (page_recurrence->priv->month_day_combo != NULL);
+               g_return_if_fail (GTK_IS_COMBO_BOX (page_recurrence->priv->month_day_combo));
+               g_return_if_fail (page_recurrence->priv->month_num_combo != NULL);
+               g_return_if_fail (GTK_IS_COMBO_BOX (page_recurrence->priv->month_num_combo));
+
+               month_num = e_dialog_combo_box_get (page_recurrence->priv->month_num_combo, 
month_num_options_map);
+               month_day = e_dialog_combo_box_get (page_recurrence->priv->month_day_combo, 
month_day_options_map);
+
+               if (month_num == MONTH_NUM_LAST)
+                       month_num = -1;
+               else if (month_num != -1)
+                       month_num++;
+               else
+                       g_warn_if_reached ();
+
+               switch (month_day) {
+               case MONTH_DAY_NTH:
+                       if (month_num == -1)
+                               r.by_month_day[0] = -1;
+                       else
+                               r.by_month_day[0] = page_recurrence->priv->month_index;
+                       break;
+
+               /* Outlook 2000 uses BYDAY=TU;BYSETPOS=2, and will not
+                * accept BYDAY=2TU. So we now use the same as Outlook
+                * by default. */
+               case MONTH_DAY_MON:
+                       r.by_day[0] = ICAL_MONDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               case MONTH_DAY_TUE:
+                       r.by_day[0] = ICAL_TUESDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               case MONTH_DAY_WED:
+                       r.by_day[0] = ICAL_WEDNESDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               case MONTH_DAY_THU:
+                       r.by_day[0] = ICAL_THURSDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               case MONTH_DAY_FRI:
+                       r.by_day[0] = ICAL_FRIDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               case MONTH_DAY_SAT:
+                       r.by_day[0] = ICAL_SATURDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               case MONTH_DAY_SUN:
+                       r.by_day[0] = ICAL_SUNDAY_WEEKDAY;
+                       r.by_set_pos[0] = month_num;
+                       break;
+
+               default:
+                       g_return_if_reached ();
+               }
+
+               break;
+       }
+
+       case ICAL_YEARLY_RECURRENCE:
+               /* Nothing else is required */
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+
+       /* Ending date */
+
+       ending_type = e_dialog_combo_box_get (page_recurrence->priv->recr_ending_combo, ending_types_map);
+
+       switch (ending_type) {
+       case ENDING_FOR:
+               g_return_if_fail (page_recurrence->priv->ending_count_spin != NULL);
+               g_return_if_fail (GTK_IS_SPIN_BUTTON (page_recurrence->priv->ending_count_spin));
+
+               r.count = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (page_recurrence->priv->ending_count_spin));
+               break;
+
+       case ENDING_UNTIL:
+               g_return_if_fail (page_recurrence->priv->ending_date_edit != NULL);
+               g_return_if_fail (E_IS_DATE_EDIT (page_recurrence->priv->ending_date_edit));
+
+               /* We only allow a DATE value to be set for the UNTIL property,
+                * since we don't support sub-day recurrences. */
+               date_set = e_date_edit_get_date (
+                       E_DATE_EDIT (page_recurrence->priv->ending_date_edit),
+                       &r.until.year,
+                       &r.until.month,
+                       &r.until.day);
+               g_return_if_fail (date_set);
+
+               r.until.is_date = 1;
+
+               break;
+
+       case ENDING_FOREVER:
+               /* Nothing to be done */
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+
+       cal_comp_util_remove_all_properties (component, ICAL_RRULE_PROPERTY);
+
+       /* Set the recurrence */
+       prop = icalproperty_new_rrule (r);
+       icalcomponent_add_property (component, prop);
+}
+
+static void
+ecep_recurrence_sensitize_widgets (ECompEditorPage *page,
+                                  gboolean force_insensitive)
+{
+       ECompEditorPageRecurrence *page_recurrence;
+       GtkTreeSelection *selection;
+       gboolean create_recurrence, any_selected;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page));
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_recurrence_parent_class)->sensitize_widgets (page, 
force_insensitive);
+
+       page_recurrence = E_COMP_EDITOR_PAGE_RECURRENCE (page);
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_recurrence->priv->exceptions_tree_view));
+
+       force_insensitive = force_insensitive || page_recurrence->priv->is_custom;
+       create_recurrence = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_recurrence->priv->recr_check_box));
+       any_selected = gtk_tree_selection_count_selected_rows (selection) > 0;
+
+       gtk_widget_set_sensitive (page_recurrence->priv->recr_check_box, !force_insensitive);
+       gtk_widget_set_sensitive (page_recurrence->priv->recr_hbox, !force_insensitive && create_recurrence);
+
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_tree_view, !force_insensitive && 
create_recurrence);
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_button_box, !force_insensitive && 
create_recurrence);
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_add_button, create_recurrence);
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_edit_button, any_selected);
+       gtk_widget_set_sensitive (page_recurrence->priv->exceptions_remove_button, any_selected);
+
+       if (page_recurrence->priv->is_custom) {
+               gtk_widget_hide (page_recurrence->priv->recr_hbox);
+               gtk_widget_show (page_recurrence->priv->recr_cannot_edit_label);
+       } else {
+               gtk_widget_show (page_recurrence->priv->recr_hbox);
+               gtk_widget_hide (page_recurrence->priv->recr_cannot_edit_label);
+       }
+
+       ecep_recurrence_update_preview (page_recurrence);
+}
+
+static void
+ecep_recurrence_fill_widgets (ECompEditorPage *page,
+                             icalcomponent *component)
+{
+       ECompEditorPageRecurrence *page_recurrence;
+       struct icalrecurrencetype rrule;
+       icalproperty *prop;
+       GtkAdjustment *adj;
+       gint n_by_second, n_by_minute, n_by_hour;
+       gint n_by_day, n_by_month_day, n_by_year_day;
+       gint n_by_week_no, n_by_month, n_by_set_pos;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page));
+       g_return_if_fail (component != NULL);
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_recurrence_parent_class)->fill_widgets (page, component);
+
+       page_recurrence = E_COMP_EDITOR_PAGE_RECURRENCE (page);
+
+       switch (icalcomponent_isa (component)) {
+               case ICAL_VEVENT_COMPONENT:
+                       gtk_button_set_label (GTK_BUTTON (page_recurrence->priv->recr_check_box),
+                               /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+                               C_("ECompEditorPageRecur", "This appointment rec_urs"));
+                       break;
+               case ICAL_VTODO_COMPONENT:
+                       gtk_button_set_label (GTK_BUTTON (page_recurrence->priv->recr_check_box),
+                               /* Translators: Entire string is for example:     'This task 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+                               C_("ECompEditorPageRecur", "This task rec_urs"));
+                       break;
+               case ICAL_VJOURNAL_COMPONENT:
+                       gtk_button_set_label (GTK_BUTTON (page_recurrence->priv->recr_check_box),
+                               /* Translators: Entire string is for example:     'This memo 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+                               C_("ECompEditorPageRecur", "This memo rec_urs"));
+                       break;
+               default:
+                       gtk_button_set_label (GTK_BUTTON (page_recurrence->priv->recr_check_box),
+                               /* Translators: Entire string is for example:     'This component 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+                               C_("ECompEditorPageRecur", "This component rec_urs"));
+                       break;
+       }
+
+       /* Clean the page */
+       ecep_recurrence_clear_widgets (page_recurrence);
+       page_recurrence->priv->is_custom = FALSE;
+
+       /* Exceptions */
+       ecep_recurrence_fill_exception_widgets (page_recurrence, component);
+
+       /* Set up defaults for the special widgets */
+       ecep_recurrence_set_special_defaults (page_recurrence, component);
+
+       /* No recurrences? */
+       if (!icalcomponent_get_first_property (component, ICAL_RDATE_PROPERTY)
+           && !icalcomponent_get_first_property (component, ICAL_RRULE_PROPERTY)
+           && !icalcomponent_get_first_property (component, ICAL_EXRULE_PROPERTY)) {
+               g_signal_handlers_block_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 
0, 0, NULL, NULL, page_recurrence);
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_recurrence->priv->recr_check_box), 
FALSE);
+               g_signal_handlers_unblock_matched (page_recurrence->priv->recr_check_box, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+
+               ecep_recurrence_update_preview (page_recurrence);
+
+               return;
+       }
+
+       /* See if it is a custom set we don't support */
+
+       if ((icalcomponent_get_first_property (component, ICAL_RRULE_PROPERTY) &&
+            icalcomponent_get_next_property (component, ICAL_RRULE_PROPERTY)) ||
+           icalcomponent_get_first_property (component, ICAL_RDATE_PROPERTY) ||
+           icalcomponent_get_first_property (component, ICAL_EXRULE_PROPERTY))
+               goto custom;
+
+       /* Down to one rule, so test that one */
+
+       prop = icalcomponent_get_first_property (component, ICAL_RRULE_PROPERTY);
+       g_return_if_fail (prop != NULL);
+
+       rrule = icalproperty_get_rrule (prop);
+
+       /* Any lower frequency? */
+
+       if (rrule.freq == ICAL_SECONDLY_RECURRENCE
+           || rrule.freq == ICAL_MINUTELY_RECURRENCE
+           || rrule.freq == ICAL_HOURLY_RECURRENCE)
+               goto custom;
+
+       /* Any unusual values? */
+
+#define N_HAS_BY(field) (ecep_recurrence_count_by_xxx (field, G_N_ELEMENTS (field)))
+
+       n_by_second = N_HAS_BY (rrule.by_second);
+       n_by_minute = N_HAS_BY (rrule.by_minute);
+       n_by_hour = N_HAS_BY (rrule.by_hour);
+       n_by_day = N_HAS_BY (rrule.by_day);
+       n_by_month_day = N_HAS_BY (rrule.by_month_day);
+       n_by_year_day = N_HAS_BY (rrule.by_year_day);
+       n_by_week_no = N_HAS_BY (rrule.by_week_no);
+       n_by_month = N_HAS_BY (rrule.by_month);
+       n_by_set_pos = N_HAS_BY (rrule.by_set_pos);
+
+       if (n_by_second != 0
+           || n_by_minute != 0
+           || n_by_hour != 0)
+               goto custom;
+
+       /* Filter the funky shit based on the frequency; if there is nothing
+        * weird we can actually set the widgets.
+        */
+
+       switch (rrule.freq) {
+       case ICAL_DAILY_RECURRENCE:
+               if (n_by_day != 0
+                   || n_by_month_day != 0
+                   || n_by_year_day != 0
+                   || n_by_week_no != 0
+                   || n_by_month != 0
+                   || n_by_set_pos != 0)
+                       goto custom;
+
+               g_signal_handlers_block_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->recr_interval_unit_combo,
+                       ICAL_DAILY_RECURRENCE,
+                       freq_map);
+               g_signal_handlers_unblock_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               break;
+
+       case ICAL_WEEKLY_RECURRENCE: {
+               gint ii;
+               guint8 day_mask;
+
+               if (n_by_month_day != 0
+                   || n_by_year_day != 0
+                   || n_by_week_no != 0
+                   || n_by_month != 0
+                   || n_by_set_pos != 0)
+                       goto custom;
+
+               day_mask = 0;
+
+               for (ii = 0; ii < 8 && rrule.by_day[ii] != ICAL_RECURRENCE_ARRAY_MAX; ii++) {
+                       enum icalrecurrencetype_weekday weekday;
+                       gint pos;
+
+                       weekday = icalrecurrencetype_day_day_of_week (rrule.by_day[ii]);
+                       pos = icalrecurrencetype_day_position (rrule.by_day[ii]);
+
+                       if (pos != 0)
+                               goto custom;
+
+                       switch (weekday) {
+                       case ICAL_SUNDAY_WEEKDAY:
+                               day_mask |= 1 << 0;
+                               break;
+
+                       case ICAL_MONDAY_WEEKDAY:
+                               day_mask |= 1 << 1;
+                               break;
+
+                       case ICAL_TUESDAY_WEEKDAY:
+                               day_mask |= 1 << 2;
+                               break;
+
+                       case ICAL_WEDNESDAY_WEEKDAY:
+                               day_mask |= 1 << 3;
+                               break;
+
+                       case ICAL_THURSDAY_WEEKDAY:
+                               day_mask |= 1 << 4;
+                               break;
+
+                       case ICAL_FRIDAY_WEEKDAY:
+                               day_mask |= 1 << 5;
+                               break;
+
+                       case ICAL_SATURDAY_WEEKDAY:
+                               day_mask |= 1 << 6;
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
+
+               page_recurrence->priv->weekday_day_mask = day_mask;
+
+               g_signal_handlers_block_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->recr_interval_unit_combo,
+                       ICAL_WEEKLY_RECURRENCE,
+                       freq_map);
+               g_signal_handlers_unblock_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               break;
+       }
+
+       case ICAL_MONTHLY_RECURRENCE:
+               if (n_by_year_day != 0
+                   || n_by_week_no != 0
+                   || n_by_month != 0
+                   || n_by_set_pos > 1)
+                       goto custom;
+
+               if (n_by_month_day == 1) {
+                       gint nth;
+
+                       if (n_by_set_pos != 0)
+                               goto custom;
+
+                       nth = rrule.by_month_day[0];
+                       if (nth < 1 && nth != -1)
+                               goto custom;
+
+                       if (nth == -1) {
+                               struct icaltimetype dtstart;
+
+                               dtstart = icalcomponent_get_dtstart (component);
+
+                               page_recurrence->priv->month_index = dtstart.day;
+                               page_recurrence->priv->month_num = MONTH_NUM_LAST;
+                       } else {
+                               page_recurrence->priv->month_index = nth;
+                               page_recurrence->priv->month_num = MONTH_NUM_DAY;
+                       }
+                       page_recurrence->priv->month_day = MONTH_DAY_NTH;
+
+               } else if (n_by_day == 1) {
+                       enum icalrecurrencetype_weekday weekday;
+                       gint pos;
+                       enum month_day_options month_day;
+
+                       /* Outlook 2000 uses BYDAY=TU;BYSETPOS=2, and will not
+                        * accept BYDAY=2TU. So we now use the same as Outlook
+                        * by default. */
+
+                       weekday = icalrecurrencetype_day_day_of_week (rrule.by_day[0]);
+                       pos = icalrecurrencetype_day_position (rrule.by_day[0]);
+
+                       if (pos == 0) {
+                               if (n_by_set_pos != 1)
+                                       goto custom;
+                               pos = rrule.by_set_pos[0];
+                       } else if (pos < 0) {
+                               goto custom;
+                       }
+
+                       switch (weekday) {
+                       case ICAL_MONDAY_WEEKDAY:
+                               month_day = MONTH_DAY_MON;
+                               break;
+
+                       case ICAL_TUESDAY_WEEKDAY:
+                               month_day = MONTH_DAY_TUE;
+                               break;
+
+                       case ICAL_WEDNESDAY_WEEKDAY:
+                               month_day = MONTH_DAY_WED;
+                               break;
+
+                       case ICAL_THURSDAY_WEEKDAY:
+                               month_day = MONTH_DAY_THU;
+                               break;
+
+                       case ICAL_FRIDAY_WEEKDAY:
+                               month_day = MONTH_DAY_FRI;
+                               break;
+
+                       case ICAL_SATURDAY_WEEKDAY:
+                               month_day = MONTH_DAY_SAT;
+                               break;
+
+                       case ICAL_SUNDAY_WEEKDAY:
+                               month_day = MONTH_DAY_SUN;
+                               break;
+
+                       default:
+                               goto custom;
+                       }
+
+                       if (pos == -1)
+                               page_recurrence->priv->month_num = MONTH_NUM_LAST;
+                       else
+                               page_recurrence->priv->month_num = pos - 1;
+                       page_recurrence->priv->month_day = month_day;
+               } else
+                       goto custom;
+
+               g_signal_handlers_block_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->recr_interval_unit_combo,
+                       ICAL_MONTHLY_RECURRENCE,
+                       freq_map);
+               g_signal_handlers_unblock_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               break;
+
+       case ICAL_YEARLY_RECURRENCE:
+               if (n_by_day != 0
+                   || n_by_month_day != 0
+                   || n_by_year_day != 0
+                   || n_by_week_no != 0
+                   || n_by_month != 0
+                   || n_by_set_pos != 0)
+                       goto custom;
+
+               g_signal_handlers_block_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               e_dialog_combo_box_set (
+                       page_recurrence->priv->recr_interval_unit_combo,
+                       ICAL_YEARLY_RECURRENCE,
+                       freq_map);
+               g_signal_handlers_unblock_matched (page_recurrence->priv->recr_interval_unit_combo, 
G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+               break;
+
+       default:
+               goto custom;
+       }
+
+       /* If we got here it means it is a simple recurrence */
+
+       g_signal_handlers_block_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_recurrence->priv->recr_check_box), TRUE);
+       g_signal_handlers_unblock_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+
+       ecep_recurrence_make_recurrence_special (page_recurrence);
+
+       adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON 
(page_recurrence->priv->recr_interval_value_spin));
+       g_signal_handlers_block_matched (adj, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_recurrence->priv->recr_interval_value_spin), 
rrule.interval);
+       g_signal_handlers_unblock_matched (adj, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page_recurrence);
+
+       ecep_recurrence_fill_ending_date (page_recurrence, &rrule, component);
+
+       return;
+
+ custom:
+
+       g_signal_handlers_block_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+       page_recurrence->priv->is_custom = TRUE;
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_recurrence->priv->recr_check_box), TRUE);
+       g_signal_handlers_unblock_matched (page_recurrence->priv->recr_check_box, G_SIGNAL_MATCH_DATA, 0, 0, 
NULL, NULL, page_recurrence);
+}
+
+static gboolean
+ecep_recurrence_fill_component (ECompEditorPage *page,
+                               icalcomponent *component)
+{
+       ECompEditorPageRecurrence *page_recurrence;
+       ECompEditor *comp_editor;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       icalproperty *prop;
+       gboolean recurs;
+       gboolean valid_iter;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       page_recurrence = E_COMP_EDITOR_PAGE_RECURRENCE (page);
+
+       recurs = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (page_recurrence->priv->recr_check_box));
+
+       if (recurs && page_recurrence->priv->is_custom) {
+               /* We just keep whatever the component has currently */
+               return TRUE;
+       } else if (recurs) {
+               cal_comp_util_remove_all_properties (component, ICAL_RRULE_PROPERTY);
+               cal_comp_util_remove_all_properties (component, ICAL_RDATE_PROPERTY);
+               cal_comp_util_remove_all_properties (component, ICAL_EXRULE_PROPERTY);
+               cal_comp_util_remove_all_properties (component, ICAL_EXDATE_PROPERTY);
+               ecep_recurrence_simple_recur_to_comp (page_recurrence, component);
+       } else {
+               gboolean had_recurrences = e_cal_util_component_has_recurrences (component);
+
+               cal_comp_util_remove_all_properties (component, ICAL_RRULE_PROPERTY);
+               cal_comp_util_remove_all_properties (component, ICAL_RDATE_PROPERTY);
+               cal_comp_util_remove_all_properties (component, ICAL_EXRULE_PROPERTY);
+               cal_comp_util_remove_all_properties (component, ICAL_EXDATE_PROPERTY);
+
+               if (had_recurrences)
+                       cal_comp_util_remove_all_properties (component, ICAL_RECURRENCEID_PROPERTY);
+
+               return TRUE;
+       }
+
+       comp_editor = e_comp_editor_page_ref_editor (page);
+
+       /* Set exceptions */
+
+       model = GTK_TREE_MODEL (page_recurrence->priv->exceptions_store);
+
+       for (valid_iter = gtk_tree_model_get_iter_first (model, &iter); valid_iter;
+            valid_iter = gtk_tree_model_iter_next (model, &iter)) {
+               const icaltimetype *dt;
+
+               dt = e_date_time_list_get_date_time (E_DATE_TIME_LIST (model), &iter);
+               g_return_val_if_fail (dt != NULL, FALSE);
+
+               if (!icaltime_is_valid_time (*dt)) {
+                       e_comp_editor_set_validation_error (comp_editor,
+                               page, page_recurrence->priv->exceptions_tree_view,
+                               _("Recurrence exception date is invalid"));
+                       g_clear_object (&comp_editor);
+                       return FALSE;
+               }
+
+               prop = icalproperty_new_exdate (*dt);
+               cal_comp_util_update_tzid_parameter (prop, *dt);
+
+               icalcomponent_add_property (component, prop);
+       }
+
+       if (gtk_widget_get_visible (page_recurrence->priv->recr_ending_combo) &&
+           gtk_widget_get_sensitive (page_recurrence->priv->recr_ending_combo) &&
+           e_dialog_combo_box_get (page_recurrence->priv->recr_ending_combo, ending_types_map) == 
ENDING_UNTIL) {
+               /* check whether the "until" date is in the future */
+               struct icaltimetype tt = icaltime_null_time ();
+               gboolean ok = TRUE;
+
+               if (e_date_edit_get_date (E_DATE_EDIT (page_recurrence->priv->ending_date_edit), &tt.year, 
&tt.month, &tt.day)) {
+                       ECompEditorPropertyPart *dtstart_part = NULL;
+                       struct icaltimetype dtstart = icaltime_null_time ();
+
+                       e_comp_editor_get_time_parts (comp_editor, &dtstart_part, NULL);
+                       if (dtstart_part) {
+                               dtstart = e_comp_editor_property_part_datetime_get_value (
+                                       E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part));
+                       }
+
+                       tt.is_date = 1;
+                       tt.zone = NULL;
+
+                       if (icaltime_is_valid_time (dtstart)) {
+                               ok = icaltime_compare_date_only (dtstart, tt) <= 0;
+
+                               if (!ok) {
+                                       e_date_edit_set_date (E_DATE_EDIT 
(page_recurrence->priv->ending_date_edit),
+                                               dtstart.year, dtstart.month, dtstart.day);
+                               } else {
+                                       /* to have the date shown in "normalized" format */
+                                       e_date_edit_set_date (E_DATE_EDIT 
(page_recurrence->priv->ending_date_edit),
+                                               tt.year, tt.month, tt.day);
+                               }
+                       }
+               }
+
+               if (!ok) {
+                       e_comp_editor_set_validation_error (comp_editor,
+                               page, page_recurrence->priv->ending_date_edit,
+                               _("End time of the recurrence is before the start"));
+                       g_clear_object (&comp_editor);
+
+                       return FALSE;
+               }
+       }
+
+       g_clear_object (&comp_editor);
+
+       return E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_recurrence_parent_class)->fill_component (page, 
component);
+}
+
+static void
+ecep_recurrence_select_page_cb (GtkAction *action,
+                               ECompEditorPage *page)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page));
+
+       e_comp_editor_page_select (page);
+}
+
+static void
+ecep_recurrence_setup_ui (ECompEditorPageRecurrence *page_recurrence)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='tabs'>"
+               "        <menuitem action='page-recurrence'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "  <toolbar name='main-toolbar'>"
+               "    <placeholder name='content'>\n"
+               "      <toolitem action='page-recurrence'/>\n"
+               "    </placeholder>"
+               "  </toolbar>"
+               "</ui>";
+
+       const GtkActionEntry options_actions[] = {
+               { "page-recurrence",
+                 "stock_task-recurring",
+                 N_("R_ecurrence"),
+                 NULL,
+                 N_("Set or unset recurrence"),
+                 G_CALLBACK (ecep_recurrence_select_page_cb) }
+       };
+
+       ECompEditor *comp_editor;
+       GtkUIManager *ui_manager;
+       GtkActionGroup *action_group;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_RECURRENCE (page_recurrence));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_recurrence));
+       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,
+               options_actions, G_N_ELEMENTS (options_actions), page_recurrence);
+
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+       g_clear_object (&comp_editor);
+
+       if (error) {
+               g_critical ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+static void
+ecep_recurrence_constructed (GObject *object)
+{
+       ECompEditorPageRecurrence *page_recurrence;
+       ECompEditor *comp_editor;
+       GtkWidget *widget, *container;
+       GtkComboBoxText *text_combo;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *cell_renderer;
+       PangoAttrList *bold;
+       GtkGrid *grid;
+       ECalendar *ecal;
+
+       G_OBJECT_CLASS (e_comp_editor_page_recurrence_parent_class)->constructed (object);
+
+       page_recurrence = E_COMP_EDITOR_PAGE_RECURRENCE (object);
+       grid = GTK_GRID (page_recurrence);
+
+       bold = pango_attr_list_new ();
+       pango_attr_list_insert (bold, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+
+       widget = gtk_label_new (_("Recurrence"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "attributes", bold,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 0, 2, 1);
+
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       widget = gtk_check_button_new_with_mnemonic (C_("ECompEditorPageRecur", "This appointment rec_urs"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+       page_recurrence->priv->recr_check_box = widget;
+
+       g_signal_connect (page_recurrence->priv->recr_check_box, "toggled",
+               G_CALLBACK (ecep_recurrence_checkbox_toggled_cb), page_recurrence);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 2, 2, 1);
+       page_recurrence->priv->recr_hbox = widget;
+
+       container = page_recurrence->priv->recr_hbox;
+
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       widget = gtk_label_new (C_("ECompEditorPageRecur", "Every"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+
+       widget = gtk_spin_button_new_with_range (1, 999, 1);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "digits", 0,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_recurrence->priv->recr_interval_value_spin = widget;
+
+       widget = gtk_combo_box_text_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_recurrence->priv->recr_interval_unit_combo = widget;
+
+       text_combo = GTK_COMBO_BOX_TEXT (widget);
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "day(s)"));
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "week(s)"));
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "month(s)"));
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "year(s)"));
+
+       g_signal_connect_swapped (page_recurrence->priv->recr_interval_unit_combo, "changed",
+               G_CALLBACK (ecep_recurrence_make_recurrence_special), page_recurrence);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_recurrence->priv->recr_interval_special_box = widget;
+
+       widget = gtk_combo_box_text_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_recurrence->priv->recr_ending_combo = widget;
+
+       text_combo = GTK_COMBO_BOX_TEXT (widget);
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "for"));
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "until"));
+       /* Translators: Entire string is for example:     'This appointment 
recurs/Every[x][day(s)][for][1]occurrences' (combobox options are in [square brackets]) */
+       gtk_combo_box_text_append_text (text_combo, C_("ECompEditorPageRecur", "forever"));
+
+       g_signal_connect_swapped (page_recurrence->priv->recr_ending_combo, "changed",
+               G_CALLBACK (ecep_recurrence_make_ending_special), page_recurrence);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_recurrence->priv->recr_ending_special_box = widget;
+
+       widget = gtk_label_new (_("This appointment contains recurrences that Evolution cannot edit."));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               NULL);
+       gtk_widget_hide (widget);
+       gtk_grid_attach (grid, widget, 0, 3, 2, 1);
+       page_recurrence->priv->recr_cannot_edit_label = widget;
+
+       widget = gtk_label_new (_("Exceptions"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "attributes", bold,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 4, 2, 1);
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "shadow-type", GTK_SHADOW_IN,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 5, 1, 1);
+
+       container = widget;
+
+       page_recurrence->priv->exceptions_store = e_date_time_list_new ();
+
+       widget = gtk_tree_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "model", page_recurrence->priv->exceptions_store,
+               "headers-visible", FALSE,
+               NULL);
+       gtk_widget_show (widget);
+
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       page_recurrence->priv->exceptions_tree_view = widget;
+
+       column = gtk_tree_view_column_new ();
+       gtk_tree_view_column_set_title (column, "Date/Time");
+       cell_renderer = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "text", 
E_DATE_TIME_LIST_COLUMN_DESCRIPTION);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (page_recurrence->priv->exceptions_tree_view), column);
+
+       g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW 
(page_recurrence->priv->exceptions_tree_view)),
+               "changed", G_CALLBACK (ecep_recurrence_exceptions_selection_changed_cb), page_recurrence);
+
+       widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 1, 5, 1, 1);
+       page_recurrence->priv->exceptions_button_box = widget;
+
+       widget = gtk_button_new_with_mnemonic (_("A_dd"));
+       gtk_box_pack_start (GTK_BOX (page_recurrence->priv->exceptions_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       page_recurrence->priv->exceptions_add_button = widget;
+
+       g_signal_connect (page_recurrence->priv->exceptions_add_button, "clicked",
+               G_CALLBACK (ecep_recurrence_exceptions_add_clicked_cb), page_recurrence);
+
+       widget = gtk_button_new_with_mnemonic (_("_Edit"));
+       gtk_box_pack_start (GTK_BOX (page_recurrence->priv->exceptions_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       page_recurrence->priv->exceptions_edit_button = widget;
+
+       g_signal_connect (page_recurrence->priv->exceptions_edit_button, "clicked",
+               G_CALLBACK (ecep_recurrence_exceptions_edit_clicked_cb), page_recurrence);
+
+       widget = gtk_button_new_with_mnemonic (_("Re_move"));
+       gtk_box_pack_start (GTK_BOX (page_recurrence->priv->exceptions_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       page_recurrence->priv->exceptions_remove_button = widget;
+
+       g_signal_connect (page_recurrence->priv->exceptions_remove_button, "clicked",
+               G_CALLBACK (ecep_recurrence_exceptions_remove_clicked_cb), page_recurrence);
+
+       widget = gtk_label_new (_("Preview"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "attributes", bold,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 6, 2, 1);
+
+       widget = e_calendar_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 7, 2, 1);
+       page_recurrence->priv->preview = widget;
+
+       pango_attr_list_unref (bold);
+
+       ecal = E_CALENDAR (page_recurrence->priv->preview);
+       g_signal_connect_swapped (
+               ecal->calitem, "date-range-changed",
+               G_CALLBACK (ecep_recurrence_update_preview), page_recurrence);
+       e_calendar_item_set_max_days_sel (ecal->calitem, 0);
+       e_calendar_item_set_get_time_callback (ecal->calitem, ecep_recurrence_get_current_time_cb, NULL, 
NULL);
+
+       g_signal_connect_swapped (page_recurrence->priv->recr_interval_value_spin, "value-changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+       g_signal_connect_swapped (page_recurrence->priv->recr_interval_unit_combo, "changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+       g_signal_connect_swapped (page_recurrence->priv->recr_ending_combo, "changed",
+               G_CALLBACK (ecep_recurrence_changed), page_recurrence);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_recurrence));
+       if (comp_editor) {
+               g_signal_connect_swapped (comp_editor, "times-changed",
+                       G_CALLBACK (ecep_recurrence_update_preview), page_recurrence);
+               g_clear_object (&comp_editor);
+       }
+
+       ecep_recurrence_setup_ui (page_recurrence);
+}
+
+static void
+ecep_recurrence_dispose (GObject *object)
+{
+       ECompEditorPageRecurrence *page_recurrence;
+       ECompEditor *comp_editor;
+
+       page_recurrence = E_COMP_EDITOR_PAGE_RECURRENCE (object);
+
+       if (page_recurrence->priv->cancellable) {
+               g_cancellable_cancel (page_recurrence->priv->cancellable);
+               g_clear_object (&page_recurrence->priv->cancellable);
+       }
+
+       g_clear_object (&page_recurrence->priv->exceptions_store);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_recurrence));
+       if (comp_editor) {
+               g_signal_handlers_disconnect_by_func (comp_editor,
+                       G_CALLBACK (ecep_recurrence_update_preview), page_recurrence);
+               g_clear_object (&comp_editor);
+       }
+
+       G_OBJECT_CLASS (e_comp_editor_page_recurrence_parent_class)->dispose (object);
+}
+
+static void
+e_comp_editor_page_recurrence_init (ECompEditorPageRecurrence *page_recurrence)
+{
+       page_recurrence->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_recurrence,
+               E_TYPE_COMP_EDITOR_PAGE_RECURRENCE,
+               ECompEditorPageRecurrencePrivate);
+
+       page_recurrence->priv->cancellable = g_cancellable_new ();
+}
+
+static void
+e_comp_editor_page_recurrence_class_init (ECompEditorPageRecurrenceClass *klass)
+{
+       ECompEditorPageClass *page_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPageRecurrencePrivate));
+
+       page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
+       page_class->sensitize_widgets = ecep_recurrence_sensitize_widgets;
+       page_class->fill_widgets = ecep_recurrence_fill_widgets;
+       page_class->fill_component = ecep_recurrence_fill_component;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = ecep_recurrence_constructed;
+       object_class->dispose = ecep_recurrence_dispose;
+}
+
+ECompEditorPage *
+e_comp_editor_page_recurrence_new (ECompEditor *editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
+
+       return g_object_new (E_TYPE_COMP_EDITOR_PAGE_RECURRENCE,
+               "editor", editor,
+               NULL);
+}
diff --git a/calendar/gui/e-comp-editor-page-recurrence.h b/calendar/gui/e-comp-editor-page-recurrence.h
new file mode 100644
index 0000000..0288459
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-recurrence.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PAGE_RECURRENCE_H
+#define E_COMP_EDITOR_PAGE_RECURRENCE_H
+
+#include <calendar/gui/e-comp-editor.h>
+#include <calendar/gui/e-comp-editor-page.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PAGE_RECURRENCE \
+       (e_comp_editor_page_recurrence_get_type ())
+#define E_COMP_EDITOR_PAGE_RECURRENCE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_RECURRENCE, ECompEditorPageRecurrence))
+#define E_COMP_EDITOR_PAGE_RECURRENCE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_RECURRENCE, ECompEditorPageRecurrenceClass))
+#define E_IS_COMP_EDITOR_PAGE_RECURRENCE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_RECURRENCE))
+#define E_IS_COMP_EDITOR_PAGE_RECURRENCE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_RECURRENCE))
+#define E_COMP_EDITOR_PAGE_RECURRENCE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_RECURRENCE, ECompEditorPageRecurrenceClass))
+
+typedef struct _ECompEditorPageRecurrence ECompEditorPageRecurrence;
+typedef struct _ECompEditorPageRecurrenceClass ECompEditorPageRecurrenceClass;
+typedef struct _ECompEditorPageRecurrencePrivate ECompEditorPageRecurrencePrivate;
+
+struct _ECompEditorPageRecurrence {
+       ECompEditorPage parent;
+
+       ECompEditorPageRecurrencePrivate *priv;
+};
+
+struct _ECompEditorPageRecurrenceClass {
+       ECompEditorPageClass parent_class;
+};
+
+GType          e_comp_editor_page_recurrence_get_type  (void) G_GNUC_CONST;
+ECompEditorPage *
+               e_comp_editor_page_recurrence_new       (ECompEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PAGE_RECURRENCE_H */
diff --git a/calendar/gui/e-comp-editor-page-reminders.c b/calendar/gui/e-comp-editor-page-reminders.c
new file mode 100644
index 0000000..8c13d54
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-reminders.c
@@ -0,0 +1,2339 @@
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *     Federico Mena-Quintero <federico ximian com>
+ *     Miguel de Icaza <miguel ximian com>
+ *     Seth Alves <alves hungry com>
+ *     JP Rosevear <jpr ximian com>
+ *     Hans Petter Jansson <hpj ximian com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "calendar-config.h"
+#include "e-alarm-list.h"
+#include "itip-utils.h"
+
+#include "e-comp-editor-page-reminders.h"
+
+#define SECTION_NAME                   _("Send To")
+#define X_EVOLUTION_NEEDS_DESCRIPTION  "X-EVOLUTION-NEEDS-DESCRIPTION"
+
+enum {
+       ALARM_NONE,
+       ALARM_15_MINUTES,
+       ALARM_1_HOUR,
+       ALARM_1_DAY,
+       ALARM_USER_TIME,
+       ALARM_CUSTOM
+};
+
+static const gint alarm_map_with_user_time[] = {
+       ALARM_NONE,
+       ALARM_15_MINUTES,
+       ALARM_1_HOUR,
+       ALARM_1_DAY,
+       ALARM_USER_TIME,
+       ALARM_CUSTOM,
+       -1
+};
+
+static const gint alarm_map_without_user_time[] = {
+       ALARM_NONE,
+       ALARM_15_MINUTES,
+       ALARM_1_HOUR,
+       ALARM_1_DAY,
+       ALARM_CUSTOM,
+       -1
+};
+
+/* "relative" types */
+enum {
+       BEFORE,
+       AFTER
+};
+
+/* Time units */
+enum {
+       MINUTES,
+       HOURS,
+       DAYS
+};
+
+/* Combo box maps */
+static const gint action_map[] = {
+       E_CAL_COMPONENT_ALARM_DISPLAY,
+       E_CAL_COMPONENT_ALARM_AUDIO,
+       E_CAL_COMPONENT_ALARM_PROCEDURE,
+       E_CAL_COMPONENT_ALARM_EMAIL,
+       -1
+};
+
+static const gchar *action_map_cap[] = {
+       CAL_STATIC_CAPABILITY_NO_DISPLAY_ALARMS,
+       CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS,
+       CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS,
+       CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS
+};
+
+static const gint value_map[] = {
+       MINUTES,
+       HOURS,
+       DAYS,
+       -1
+};
+
+static const gint relative_map[] = {
+       BEFORE,
+       AFTER,
+       -1
+};
+
+static const gint time_map[] = {
+       E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START,
+       E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END,
+       -1
+};
+
+enum duration_units {
+       DUR_MINUTES,
+       DUR_HOURS,
+       DUR_DAYS
+};
+
+static const gint duration_units_map[] = {
+       DUR_MINUTES,
+       DUR_HOURS,
+       DUR_DAYS,
+       -1
+};
+
+struct _ECompEditorPageRemindersPrivate {
+       GtkWidget *alarms_combo;
+       GtkWidget *alarms_scrolled_window;
+       GtkWidget *alarms_tree_view;
+       GtkWidget *alarms_button_box;
+       GtkWidget *alarms_add_button;
+       GtkWidget *alarms_remove_button;
+
+       GtkWidget *alarm_setup_hbox;
+       GtkWidget *kind_combo;
+       GtkWidget *time_spin;
+       GtkWidget *unit_combo;
+       GtkWidget *relative_time_combo;
+       GtkWidget *relative_to_combo;
+       GtkWidget *repeat_setup_hbox;
+       GtkWidget *repeat_check;
+       GtkWidget *repeat_times_spin;
+       GtkWidget *repeat_every_label;
+       GtkWidget *repeat_every_spin;
+       GtkWidget *repeat_unit_combo;
+       GtkWidget *options_label;
+       GtkWidget *options_notebook;
+       GtkWidget *custom_message_check;
+       GtkWidget *custom_message_text_view;
+       GtkWidget *custom_sound_check;
+       GtkWidget *custom_sound_chooser;
+       GtkWidget *custom_app_path_entry;
+       GtkWidget *custom_app_args_entry;
+       GtkWidget *custom_email_button;
+       GtkWidget *custom_email_entry;
+       GtkWidget *custom_email_message_check;
+       GtkWidget *custom_email_message_text_view;
+
+       EAlarmList *alarm_list;
+       EDurationType alarm_units;
+       gint alarm_interval;
+       /* either with-user-time or without it */
+       const gint *alarm_map;
+
+       /* Addressbook name selector, created on demand */
+       ENameSelector *name_selector;
+};
+
+G_DEFINE_TYPE (ECompEditorPageReminders, e_comp_editor_page_reminders, E_TYPE_COMP_EDITOR_PAGE)
+
+static void
+ecep_reminders_sanitize_option_widgets (ECompEditorPageReminders *page_reminders)
+{
+       gboolean any_selected;
+       gboolean is_custom;
+       gboolean sensitive;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       any_selected = gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (
+               GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view))) > 0;
+       is_custom = e_dialog_combo_box_get (page_reminders->priv->alarms_combo,
+               page_reminders->priv->alarm_map) == ALARM_CUSTOM;
+
+       gtk_widget_set_sensitive (page_reminders->priv->alarms_tree_view, is_custom);
+       gtk_widget_set_sensitive (page_reminders->priv->alarms_add_button, is_custom);
+       gtk_widget_set_sensitive (page_reminders->priv->alarms_remove_button, any_selected && is_custom);
+
+       gtk_widget_set_visible (page_reminders->priv->alarm_setup_hbox, any_selected && is_custom);
+       gtk_widget_set_visible (page_reminders->priv->repeat_setup_hbox, any_selected && is_custom);
+       gtk_widget_set_visible (page_reminders->priv->options_label, any_selected && is_custom);
+       gtk_widget_set_visible (page_reminders->priv->options_notebook, any_selected && is_custom);
+
+       sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (page_reminders->priv->repeat_check));
+       gtk_widget_set_sensitive (page_reminders->priv->repeat_times_spin, sensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->repeat_every_label, sensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->repeat_every_spin, sensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->repeat_unit_combo, sensitive);
+
+       sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_message_check));
+       gtk_widget_set_sensitive (page_reminders->priv->custom_message_text_view, sensitive);
+
+       sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_sound_check));
+       gtk_widget_set_sensitive (page_reminders->priv->custom_sound_chooser, sensitive);
+
+       sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_email_message_check));
+       gtk_widget_set_sensitive (page_reminders->priv->custom_email_message_text_view, sensitive);
+}
+
+static void
+ecep_reminders_set_text_view_text (GtkWidget *text_view,
+                                  const gchar *text)
+{
+       GtkTextBuffer *text_buffer;
+
+       g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+       text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+       gtk_text_buffer_set_text (text_buffer, text ? text : "", -1);
+}
+
+static gchar *
+ecep_reminders_get_text_view_text (GtkWidget *text_view)
+{
+       GtkTextBuffer *text_buffer;
+       GtkTextIter text_iter_start, text_iter_end;
+
+       g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+
+       text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+       gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start);
+       gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
+
+       return gtk_text_buffer_get_text (text_buffer, &text_iter_start, &text_iter_end, FALSE);
+}
+
+static void
+ecep_reminders_remove_needs_description_property (ECalComponentAlarm *alarm)
+{
+       icalcomponent *component;
+       icalproperty *prop;
+
+       g_return_if_fail (alarm != NULL);
+
+       component = e_cal_component_alarm_get_icalcomponent (alarm);
+       g_return_if_fail (component != NULL);
+
+       for (prop = icalcomponent_get_first_property (component, ICAL_X_PROPERTY);
+            prop;
+            prop = icalcomponent_get_next_property (component, ICAL_X_PROPERTY)) {
+               const gchar *x_name;
+
+               x_name = icalproperty_get_x_name (prop);
+               if (g_str_equal (x_name, X_EVOLUTION_NEEDS_DESCRIPTION)) {
+                       icalcomponent_remove_property (component, prop);
+                       icalproperty_free (prop);
+                       break;
+               }
+       }
+}
+
+static gboolean
+ecep_reminders_has_needs_description_property (ECalComponentAlarm *alarm)
+{
+       icalcomponent *component;
+       icalproperty *prop;
+
+       g_return_val_if_fail (alarm != NULL, FALSE);
+
+       component = e_cal_component_alarm_get_icalcomponent (alarm);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       for (prop = icalcomponent_get_first_property (component, ICAL_X_PROPERTY);
+            prop;
+            prop = icalcomponent_get_next_property (component, ICAL_X_PROPERTY)) {
+               const gchar *x_name;
+
+               x_name = icalproperty_get_x_name (prop);
+               if (g_str_equal (x_name, X_EVOLUTION_NEEDS_DESCRIPTION)) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static void
+ecep_reminders_add_needs_description_property (ECalComponentAlarm *alarm)
+{
+       icalcomponent *component;
+       icalproperty *prop;
+
+       g_return_if_fail (alarm != NULL);
+
+       if (ecep_reminders_has_needs_description_property (alarm))
+               return;
+
+       component = e_cal_component_alarm_get_icalcomponent (alarm);
+       g_return_if_fail (component != NULL);
+
+       prop = icalproperty_new_x ("1");
+       icalproperty_set_x_name (prop, X_EVOLUTION_NEEDS_DESCRIPTION);
+       icalcomponent_add_property (component, prop);
+}
+
+static void
+ecep_reminders_reset_alarm_widget (ECompEditorPageReminders *page_reminders)
+{
+       ECompEditorPage *page;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       page = E_COMP_EDITOR_PAGE (page_reminders);
+
+       e_comp_editor_page_set_updating (page, TRUE);
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->kind_combo), 0);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin), 15);
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->unit_combo), 0);
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->relative_time_combo), 0);
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->relative_to_combo), 0);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_reminders->priv->repeat_check), FALSE);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_times_spin), 1);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin), 5);
+       gtk_combo_box_set_active (GTK_COMBO_BOX (page_reminders->priv->repeat_unit_combo), 0);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_reminders->priv->custom_message_check), FALSE);
+       ecep_reminders_set_text_view_text (page_reminders->priv->custom_message_text_view, NULL);
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_reminders->priv->custom_sound_check), FALSE);
+       gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (page_reminders->priv->custom_sound_chooser));
+       gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_app_path_entry), "");
+       gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_app_args_entry), "");
+       if (page_reminders->priv->custom_email_entry)
+               gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_email_entry), "");
+       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_reminders->priv->custom_email_message_check), 
FALSE);
+       ecep_reminders_set_text_view_text (page_reminders->priv->custom_email_message_text_view, NULL);
+
+       e_comp_editor_page_set_updating (page, FALSE);
+}
+
+static void
+ecep_reminders_selected_to_widgets (ECompEditorPageReminders *page_reminders)
+{
+       ECalComponentAlarmTrigger trigger;
+       ECalComponentAlarmAction action;
+       ECalComponentAlarmRepeat repeat;
+       ECalComponentAlarm *alarm;
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view));
+       g_return_if_fail (gtk_tree_selection_get_selected (selection, NULL, &iter));
+
+       alarm = (ECalComponentAlarm *) e_alarm_list_get_alarm (page_reminders->priv->alarm_list, &iter);
+       g_return_if_fail (alarm != NULL);
+
+       e_cal_component_alarm_get_action (alarm, &action);
+       e_cal_component_alarm_get_trigger (alarm, &trigger);
+
+       e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_reminders), TRUE);
+
+       if (action == E_CAL_COMPONENT_ALARM_NONE) {
+               ecep_reminders_reset_alarm_widget (page_reminders);
+
+               e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_reminders), FALSE);
+               return;
+       }
+
+       /* Alarm Types */
+       switch (trigger.type) {
+       case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
+               e_dialog_combo_box_set (page_reminders->priv->relative_to_combo, 
E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START, time_map);
+               break;
+
+       case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
+               e_dialog_combo_box_set (page_reminders->priv->relative_to_combo, 
E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END, time_map);
+               break;
+       default:
+               g_warning ("%s: Unexpected alarm trigger type (%d)", G_STRLOC, trigger.type);
+       }
+
+       switch (trigger.u.rel_duration.is_neg) {
+       case 1:
+               e_dialog_combo_box_set (page_reminders->priv->relative_time_combo, BEFORE, relative_map);
+               break;
+
+       case 0:
+               e_dialog_combo_box_set (page_reminders->priv->relative_time_combo, AFTER, relative_map);
+               break;
+       }
+
+       if (trigger.u.rel_duration.days) {
+               e_dialog_combo_box_set (page_reminders->priv->unit_combo, DAYS, value_map);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin),
+                       trigger.u.rel_duration.days);
+       } else if (trigger.u.rel_duration.hours) {
+               e_dialog_combo_box_set (page_reminders->priv->unit_combo, HOURS, value_map);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin),
+                       trigger.u.rel_duration.hours);
+       } else if (trigger.u.rel_duration.minutes) {
+               e_dialog_combo_box_set (page_reminders->priv->unit_combo, MINUTES, value_map);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin),
+                       trigger.u.rel_duration.minutes);
+       } else {
+               e_dialog_combo_box_set (page_reminders->priv->unit_combo, MINUTES, value_map);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->time_spin), 0);
+       }
+
+       /* Repeat options */
+       e_cal_component_alarm_get_repeat (alarm, &repeat);
+
+       if (repeat.repetitions) {
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_reminders->priv->repeat_check), TRUE);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_times_spin),
+                       repeat.repetitions);
+
+               if (repeat.duration.minutes) {
+                       e_dialog_combo_box_set (page_reminders->priv->repeat_unit_combo, DUR_MINUTES, 
duration_units_map);
+                       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin),
+                               repeat.duration.minutes);
+               }
+
+               if (repeat.duration.hours) {
+                       e_dialog_combo_box_set (page_reminders->priv->repeat_unit_combo, DUR_HOURS, 
duration_units_map);
+                       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin),
+                               repeat.duration.hours);
+               }
+
+               if (repeat.duration.days) {
+                       e_dialog_combo_box_set (page_reminders->priv->repeat_unit_combo, DUR_DAYS, 
duration_units_map);
+                       gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin),
+                               repeat.duration.days);
+               }
+       } else {
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (page_reminders->priv->repeat_check), FALSE);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_times_spin), 1);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin), 5);
+       }
+
+       /* Alarm options */
+       e_dialog_combo_box_set (page_reminders->priv->kind_combo, action, action_map);
+
+       switch (action) {
+       case E_CAL_COMPONENT_ALARM_AUDIO: {
+               const gchar *url;
+               icalattach *attach = NULL;
+
+               e_cal_component_alarm_get_attach (alarm, &attach);
+               url = attach ? icalattach_get_url (attach) : NULL;
+               if (url && *url) {
+                       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_sound_check), TRUE);
+                       gtk_file_chooser_set_uri (GTK_FILE_CHOOSER 
(page_reminders->priv->custom_sound_chooser), url);
+               } else {
+                       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_sound_check), FALSE);
+                       gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER 
(page_reminders->priv->custom_sound_chooser));
+               }
+
+               if (attach)
+                       icalattach_unref (attach);
+               } break;
+
+       case E_CAL_COMPONENT_ALARM_DISPLAY: {
+               ECalComponentText description;
+
+               e_cal_component_alarm_get_description (alarm, &description);
+
+               if (description.value && *description.value) {
+                       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_message_check), TRUE);
+                       ecep_reminders_set_text_view_text (page_reminders->priv->custom_message_text_view, 
description.value);
+               } else {
+                       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_message_check), FALSE);
+                       ecep_reminders_set_text_view_text (page_reminders->priv->custom_message_text_view, 
NULL);
+               }
+               } break;
+
+       case E_CAL_COMPONENT_ALARM_EMAIL: {
+               ENameSelectorModel *name_selector_model;
+               EDestinationStore *destination_store;
+               ECalComponentText description;
+               GSList *attendee_list, *link;
+
+               /* Attendees */
+               name_selector_model = e_name_selector_peek_model (page_reminders->priv->name_selector);
+               e_name_selector_model_peek_section (name_selector_model, SECTION_NAME, NULL, 
&destination_store);
+
+               e_cal_component_alarm_get_attendee_list (alarm, &attendee_list);
+               for (link = attendee_list; link; link = g_slist_next (link)) {
+                       ECalComponentAttendee *a = link->data;
+                       EDestination *dest;
+
+                       dest = e_destination_new ();
+
+                       if (a->cn && *a->cn)
+                               e_destination_set_name (dest, a->cn);
+
+                       if (a->value && *a->value)
+                               e_destination_set_email (dest, itip_strip_mailto (a->value));
+
+                       e_destination_store_append_destination (destination_store, dest);
+
+                       g_object_unref (dest);
+               }
+
+               e_cal_component_free_attendee_list (attendee_list);
+
+               /* Description */
+               e_cal_component_alarm_get_description (alarm, &description);
+               if (description.value && *description.value) {
+                       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_email_message_check), TRUE);
+                       ecep_reminders_set_text_view_text 
(page_reminders->priv->custom_email_message_text_view, description.value);
+               } else {
+                       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_email_message_check), FALSE);
+                       ecep_reminders_set_text_view_text 
(page_reminders->priv->custom_email_message_text_view, NULL);
+               }
+               } break;
+
+       case E_CAL_COMPONENT_ALARM_PROCEDURE: {
+               const gchar *url;
+               icalattach *attach = NULL;
+
+               e_cal_component_alarm_get_attach (alarm, (&attach));
+               url = attach ? icalattach_get_url (attach) : NULL;
+
+               if (url && *url) {
+                       ECalComponentText description;
+
+                       e_cal_component_alarm_get_description (alarm, &description);
+
+                       gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_app_path_entry), url);
+                       gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_app_args_entry), 
description.value ? description.value : "");
+               } else {
+                       gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_app_path_entry), "");
+                       gtk_entry_set_text (GTK_ENTRY (page_reminders->priv->custom_app_args_entry), "");
+               }
+
+               if (attach)
+                       icalattach_unref (attach);
+               } break;
+       default:
+               g_warning ("%s: Unexpected alarm action (%d)", G_STRLOC, action);
+       }
+
+       e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_reminders), FALSE);
+}
+
+static void
+ecep_reminders_widgets_to_selected (ECompEditorPageReminders *page_reminders)
+{
+       ECalComponentAlarmTrigger trigger;
+       ECalComponentAlarmAction action;
+       ECalComponentAlarmRepeat repeat;
+       ECalComponentAlarm *alarm;
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       if (e_comp_editor_page_get_updating (E_COMP_EDITOR_PAGE (page_reminders)))
+               return;
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view));
+       if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+               return;
+
+       alarm = e_cal_component_alarm_new ();
+
+       /* Fill out the alarm */
+       memset (&trigger, 0, sizeof (ECalComponentAlarmTrigger));
+
+       trigger.type = e_dialog_combo_box_get (page_reminders->priv->relative_to_combo, time_map);
+       if (e_dialog_combo_box_get (page_reminders->priv->relative_time_combo, relative_map) == BEFORE)
+               trigger.u.rel_duration.is_neg = 1;
+       else
+               trigger.u.rel_duration.is_neg = 0;
+
+       switch (e_dialog_combo_box_get (page_reminders->priv->unit_combo, value_map)) {
+       case MINUTES:
+               trigger.u.rel_duration.minutes = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (page_reminders->priv->time_spin));
+               break;
+
+       case HOURS:
+               trigger.u.rel_duration.hours = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (page_reminders->priv->time_spin));
+               break;
+
+       case DAYS:
+               trigger.u.rel_duration.days = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (page_reminders->priv->time_spin));
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+       e_cal_component_alarm_set_trigger (alarm, trigger);
+
+       action = e_dialog_combo_box_get (page_reminders->priv->kind_combo, action_map);
+       e_cal_component_alarm_set_action (alarm, action);
+
+       /* Repeat stuff */
+       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (page_reminders->priv->repeat_check))) {
+               repeat.repetitions = gtk_spin_button_get_value_as_int (
+                       GTK_SPIN_BUTTON (page_reminders->priv->repeat_times_spin));
+
+               memset (&repeat.duration, 0, sizeof (repeat.duration));
+
+               switch (e_dialog_combo_box_get (page_reminders->priv->repeat_unit_combo, duration_units_map)) 
{
+               case DUR_MINUTES:
+                       repeat.duration.minutes = gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin));
+                       break;
+
+               case DUR_HOURS:
+                       repeat.duration.hours = gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin));
+                       break;
+
+               case DUR_DAYS:
+                       repeat.duration.days = gtk_spin_button_get_value_as_int (
+                               GTK_SPIN_BUTTON (page_reminders->priv->repeat_every_spin));
+                       break;
+
+               default:
+                       g_return_if_reached ();
+               }
+       } else {
+               repeat.repetitions = 0;
+       }
+
+       e_cal_component_alarm_set_repeat (alarm, repeat);
+
+       /* Options */
+       switch (action) {
+       case E_CAL_COMPONENT_ALARM_NONE:
+               g_return_if_reached ();
+               break;
+
+       case E_CAL_COMPONENT_ALARM_AUDIO:
+               if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_sound_check))) {
+                       gchar *url;
+
+                       url = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER 
(page_reminders->priv->custom_sound_chooser));
+
+                       if (url && *url) {
+                               icalattach *attach;
+
+                               attach = icalattach_new_from_url (url);
+                               e_cal_component_alarm_set_attach (alarm, attach);
+                               icalattach_unref (attach);
+                       }
+
+                       g_free (url);
+
+                       url = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER 
(page_reminders->priv->custom_sound_chooser));
+                       if (url && *url) {
+                               gchar *path;
+
+                               path = g_path_get_dirname (url);
+                               if (path && *path) {
+                                       calendar_config_set_dir_path (path);
+                               }
+
+                               g_free (path);
+                       }
+
+                       g_free (url);
+               }
+               break;
+
+       case E_CAL_COMPONENT_ALARM_DISPLAY:
+               if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_message_check))) {
+                       ECalComponentText description;
+                       gchar *text;
+
+                       text = ecep_reminders_get_text_view_text 
(page_reminders->priv->custom_message_text_view);
+                       if (text && *text) {
+                               description.value = text;
+                               description.altrep = NULL;
+
+                               e_cal_component_alarm_set_description (alarm, &description);
+
+                               ecep_reminders_remove_needs_description_property (alarm);
+                       }
+
+                       g_free (text);
+               }
+               break;
+
+       case E_CAL_COMPONENT_ALARM_EMAIL: {
+               GSList *attendee_list = NULL;
+               ENameSelectorModel *name_selector_model;
+               EDestinationStore *destination_store;
+               GList *destinations, *link;
+
+               /* Attendees */
+               name_selector_model = e_name_selector_peek_model (page_reminders->priv->name_selector);
+               e_name_selector_model_peek_section (name_selector_model, SECTION_NAME, NULL, 
&destination_store);
+               destinations = e_destination_store_list_destinations (destination_store);
+
+               for (link = destinations; link; link = g_list_next (link)) {
+                       EDestination *dest = link->data;
+                       ECalComponentAttendee *a;
+
+                       a = g_new0 (ECalComponentAttendee, 1);
+                       a->value = e_destination_get_email (dest);
+                       a->cn = e_destination_get_name (dest);
+                       a->cutype = ICAL_CUTYPE_INDIVIDUAL;
+                       a->status = ICAL_PARTSTAT_NEEDSACTION;
+                       a->role = ICAL_ROLE_REQPARTICIPANT;
+
+                       attendee_list = g_slist_append (attendee_list, a);
+               }
+
+               e_cal_component_alarm_set_attendee_list (alarm, attendee_list);
+
+               e_cal_component_free_attendee_list (attendee_list);
+               g_list_free (destinations);
+
+               if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(page_reminders->priv->custom_email_message_check))) {
+                       ECalComponentText description;
+                       gchar *text;
+
+                       text = ecep_reminders_get_text_view_text 
(page_reminders->priv->custom_email_message_text_view);
+                       if (text && *text) {
+                               description.value = text;
+                               description.altrep = NULL;
+
+                               e_cal_component_alarm_set_description (alarm, &description);
+
+                               ecep_reminders_remove_needs_description_property (alarm);
+                       }
+
+                       g_free (text);
+               }
+               } break;
+
+       case E_CAL_COMPONENT_ALARM_PROCEDURE: {
+               ECalComponentText description;
+               icalattach *attach;
+               const gchar *text;
+
+               text = gtk_entry_get_text (GTK_ENTRY (page_reminders->priv->custom_app_path_entry));
+
+               attach = icalattach_new_from_url (text ? text : "");
+               e_cal_component_alarm_set_attach (alarm, attach);
+               icalattach_unref (attach);
+
+               text = gtk_entry_get_text (GTK_ENTRY (page_reminders->priv->custom_app_args_entry));
+
+               description.value = text;
+               description.altrep = NULL;
+
+               e_cal_component_alarm_set_description (alarm, &description);
+               ecep_reminders_remove_needs_description_property (alarm);
+               } break;
+
+       case E_CAL_COMPONENT_ALARM_UNKNOWN:
+               break;
+
+       default:
+               g_return_if_reached ();
+       }
+
+       e_alarm_list_set_alarm (page_reminders->priv->alarm_list, &iter, alarm);
+
+       e_cal_component_alarm_free (alarm);
+
+       e_comp_editor_page_emit_changed (E_COMP_EDITOR_PAGE (page_reminders));
+}
+
+static void
+ecep_reminders_alarms_selection_changed_cb (GtkTreeSelection *selection,
+                                           ECompEditorPageReminders *page_reminders)
+{
+       g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       ecep_reminders_sanitize_option_widgets (page_reminders);
+
+       if (gtk_tree_selection_get_selected (selection, NULL, NULL))
+               ecep_reminders_selected_to_widgets (page_reminders);
+}
+
+static void
+ecep_reminders_alarms_combo_changed_cb (GtkComboBox *combo_box,
+                                       ECompEditorPageReminders *page_reminders)
+{
+       ECalComponentAlarm *alarm;
+       ECalComponentAlarmTrigger trigger;
+       gint alarm_type;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       ecep_reminders_sanitize_option_widgets (page_reminders);
+
+       if (!e_comp_editor_page_get_updating (E_COMP_EDITOR_PAGE (page_reminders)))
+               e_comp_editor_page_emit_changed (E_COMP_EDITOR_PAGE (page_reminders));
+
+       alarm_type = e_dialog_combo_box_get (page_reminders->priv->alarms_combo, 
page_reminders->priv->alarm_map);
+       if (alarm_type == ALARM_NONE) {
+               e_alarm_list_clear (page_reminders->priv->alarm_list);
+               return;
+       }
+
+       if (alarm_type == ALARM_CUSTOM) {
+               GtkTreeSelection *selection;
+
+               selection = gtk_tree_view_get_selection (GTK_TREE_VIEW 
(page_reminders->priv->alarms_tree_view));
+
+               if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) {
+                       GtkTreeIter iter;
+
+                       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (page_reminders->priv->alarm_list), 
&iter))
+                               gtk_tree_selection_select_iter (selection, &iter);
+               }
+
+               return;
+       }
+
+       e_alarm_list_clear (page_reminders->priv->alarm_list);
+
+       alarm = e_cal_component_alarm_new ();
+
+       e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+
+       memset (&trigger, 0, sizeof (ECalComponentAlarmTrigger));
+       trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+       trigger.u.rel_duration.is_neg = 1;
+
+       switch (alarm_type) {
+       case ALARM_15_MINUTES:
+               trigger.u.rel_duration.minutes = 15;
+               break;
+
+       case ALARM_1_HOUR:
+               trigger.u.rel_duration.hours = 1;
+               break;
+
+       case ALARM_1_DAY:
+               trigger.u.rel_duration.days = 1;
+               break;
+
+       case ALARM_USER_TIME:
+               switch (page_reminders->priv->alarm_units) {
+               case E_DURATION_DAYS:
+                       trigger.u.rel_duration.days = page_reminders->priv->alarm_interval;
+                       break;
+
+               case E_DURATION_HOURS:
+                       trigger.u.rel_duration.hours = page_reminders->priv->alarm_interval;
+                       break;
+
+               case E_DURATION_MINUTES:
+                       trigger.u.rel_duration.minutes = page_reminders->priv->alarm_interval;
+                       break;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       e_cal_component_alarm_set_trigger (alarm, trigger);
+       ecep_reminders_add_needs_description_property (alarm);
+       e_alarm_list_append (page_reminders->priv->alarm_list, NULL, alarm);
+       e_cal_component_alarm_free (alarm);
+}
+
+static void
+ecep_reminders_alarms_add_clicked_cb (GtkButton *button,
+                                     ECompEditorPageReminders *page_reminders)
+{
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+       ECalComponentAlarm *alarm;
+       ECalComponentAlarmTrigger trigger;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       alarm = e_cal_component_alarm_new ();
+
+       ecep_reminders_add_needs_description_property (alarm);
+
+       memset (&trigger, 0, sizeof (ECalComponentAlarmTrigger));
+       trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+       trigger.u.rel_duration.is_neg = 1;
+       trigger.u.rel_duration.minutes = 15;
+
+       e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+       e_cal_component_alarm_set_trigger (alarm, trigger);
+
+       e_alarm_list_append (page_reminders->priv->alarm_list, &iter, alarm);
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view));
+       gtk_tree_selection_select_iter (selection, &iter);
+
+       ecep_reminders_sanitize_option_widgets (page_reminders);
+}
+
+static void
+ecep_reminders_alarms_remove_clicked_cb (GtkButton *button,
+                                        ECompEditorPageReminders *page_reminders)
+{
+       GtkTreeSelection *selection;
+       GtkTreeIter iter;
+       GtkTreePath *path;
+       GtkTreeModel *model;
+       gboolean valid_iter;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view));
+       g_return_if_fail (gtk_tree_selection_get_selected (selection, &model, &iter));
+
+       path = gtk_tree_model_get_path (model, &iter);
+
+       e_alarm_list_remove (page_reminders->priv->alarm_list, &iter);
+
+       /* Select closest item after removal */
+       valid_iter = gtk_tree_model_get_iter (model, &iter, path);
+       if (!valid_iter && gtk_tree_path_prev (path)) {
+               valid_iter = gtk_tree_model_get_iter (model, &iter, path);
+       }
+
+       if (valid_iter)
+               gtk_tree_selection_select_iter (selection, &iter);
+
+       e_comp_editor_page_emit_changed (E_COMP_EDITOR_PAGE (page_reminders));
+}
+
+static void
+ecep_reminders_name_selector_dialog_response_cb (GtkWidget *widget,
+                                                gint response,
+                                                ECompEditorPageReminders *page_reminders)
+{
+       ENameSelectorDialog *name_selector_dialog;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       name_selector_dialog = e_name_selector_peek_dialog (page_reminders->priv->name_selector);
+       gtk_widget_hide (GTK_WIDGET (name_selector_dialog));
+}
+
+static void
+ecep_reminders_set_alarm_email (ECompEditorPageReminders *page_reminders)
+{
+       ECompEditor *comp_editor;
+       ECalClient *target_client;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       if (!page_reminders->priv->name_selector)
+               return;
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_reminders));
+       target_client = e_comp_editor_get_target_client (comp_editor);
+
+       if (target_client &&
+           !e_client_check_capability (E_CLIENT (target_client), CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS)) {
+               ENameSelectorModel *selector_model;
+               EDestinationStore *destination_store = NULL;
+               const gchar *alarm_email;
+
+               alarm_email = e_comp_editor_get_alarm_email_address (comp_editor);
+               selector_model = e_name_selector_peek_model (page_reminders->priv->name_selector);
+               if (alarm_email && *alarm_email &&
+                   e_name_selector_model_peek_section (selector_model, SECTION_NAME, NULL, 
&destination_store) &&
+                   destination_store && !gtk_tree_model_iter_n_children (GTK_TREE_MODEL (destination_store), 
NULL)) {
+                       EDestination *dest;
+
+                       dest = e_destination_new ();
+
+                       e_destination_set_email (dest, alarm_email);
+                       e_destination_store_append_destination (destination_store, dest);
+
+                       g_object_unref (dest);
+               }
+       }
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_reminders_setup_name_selector (ECompEditorPageReminders *page_reminders)
+{
+       ECompEditor *comp_editor;
+       ENameSelectorModel *name_selector_model;
+       ENameSelectorDialog *name_selector_dialog;
+       GtkWidget *widget, *option_grid;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+       g_return_if_fail (page_reminders->priv->name_selector == NULL);
+       g_return_if_fail (page_reminders->priv->custom_email_entry == NULL);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_reminders));
+
+       page_reminders->priv->name_selector = e_name_selector_new (e_shell_get_client_cache 
(e_comp_editor_get_shell (comp_editor)));
+
+       e_name_selector_load_books (page_reminders->priv->name_selector);
+       name_selector_model = e_name_selector_peek_model (page_reminders->priv->name_selector);
+
+       e_name_selector_model_add_section (name_selector_model, SECTION_NAME, SECTION_NAME, NULL);
+
+       option_grid = gtk_notebook_get_nth_page (GTK_NOTEBOOK (page_reminders->priv->options_notebook), 3);
+
+       widget = GTK_WIDGET (e_name_selector_peek_section_entry (page_reminders->priv->name_selector, 
SECTION_NAME));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 4,
+               #else
+               "margin-left", 4,
+               #endif
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 1, 0, 1, 1);
+       page_reminders->priv->custom_email_entry = widget;
+
+       g_signal_connect_swapped (page_reminders->priv->custom_email_entry, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+
+       name_selector_dialog = e_name_selector_peek_dialog (page_reminders->priv->name_selector);
+       g_signal_connect (name_selector_dialog, "response",
+               G_CALLBACK (ecep_reminders_name_selector_dialog_response_cb), page_reminders);
+
+       ecep_reminders_set_alarm_email (page_reminders);
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_reminders_kind_combo_changed_cb (GtkWidget *combo_box,
+                                     ECompEditorPageReminders *page_reminders)
+{
+       ECalComponentAlarmAction action;
+       gint page = 0, ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       if (!page_reminders->priv->name_selector &&
+           e_dialog_combo_box_get (combo_box, action_map) == E_CAL_COMPONENT_ALARM_EMAIL) {
+               ecep_reminders_setup_name_selector (page_reminders);
+       }
+
+       action = e_dialog_combo_box_get (page_reminders->priv->kind_combo, action_map);
+       for (ii = 0; action_map[ii] != -1; ii++) {
+               if (action == action_map[ii]) {
+                       page = ii;
+                       break;
+               }
+       }
+
+       gtk_notebook_set_current_page (GTK_NOTEBOOK (page_reminders->priv->options_notebook), page);
+}
+
+static void
+ecep_reminders_send_to_clicked_cb (GtkWidget *button,
+                                  ECompEditorPageReminders *page_reminders)
+{
+       GtkWidget *toplevel;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+       g_return_if_fail (page_reminders->priv->name_selector != NULL);
+
+       toplevel = gtk_widget_get_toplevel (button);
+       if (!GTK_IS_WINDOW (toplevel))
+               toplevel = NULL;
+
+       e_name_selector_show_dialog (page_reminders->priv->name_selector, toplevel);
+}
+
+static gboolean
+ecep_reminders_is_custom_alarm (ECalComponentAlarm *ca,
+                               const gchar *old_summary,
+                               EDurationType user_units,
+                               gint user_interval,
+                               gint *alarm_type)
+{
+       ECalComponentAlarmTrigger trigger;
+       ECalComponentAlarmRepeat repeat;
+       ECalComponentAlarmAction action;
+       ECalComponentText desc;
+       icalattach *attach;
+
+       e_cal_component_alarm_get_action (ca, &action);
+       if (action != E_CAL_COMPONENT_ALARM_DISPLAY)
+               return TRUE;
+
+       e_cal_component_alarm_get_attach (ca, &attach);
+       if (attach)
+               return TRUE;
+
+       if (!ecep_reminders_has_needs_description_property (ca)) {
+               e_cal_component_alarm_get_description (ca, &desc);
+               if (!desc.value || !old_summary || strcmp (desc.value, old_summary))
+                       return TRUE;
+       }
+
+       e_cal_component_alarm_get_repeat (ca, &repeat);
+       if (repeat.repetitions != 0)
+               return TRUE;
+
+       if (e_cal_component_alarm_has_attendees (ca))
+               return TRUE;
+
+       e_cal_component_alarm_get_trigger (ca, &trigger);
+       if (trigger.type != E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START)
+               return TRUE;
+
+       if (trigger.u.rel_duration.is_neg != 1)
+               return TRUE;
+
+       if (trigger.u.rel_duration.weeks != 0)
+               return TRUE;
+
+       if (trigger.u.rel_duration.seconds != 0)
+               return TRUE;
+
+       if (trigger.u.rel_duration.days == 1
+           && trigger.u.rel_duration.hours == 0
+           && trigger.u.rel_duration.minutes == 0) {
+               if (alarm_type)
+                       *alarm_type = ALARM_1_DAY;
+               return FALSE;
+       }
+
+       if (trigger.u.rel_duration.days == 0
+           && trigger.u.rel_duration.hours == 1
+           && trigger.u.rel_duration.minutes == 0) {
+               if (alarm_type)
+                       *alarm_type = ALARM_1_HOUR;
+               return FALSE;
+       }
+
+       if (trigger.u.rel_duration.days == 0
+           && trigger.u.rel_duration.hours == 0
+           && trigger.u.rel_duration.minutes == 15) {
+               if (alarm_type)
+                       *alarm_type = ALARM_15_MINUTES;
+               return FALSE;
+       }
+
+       if (user_interval != -1) {
+               switch (user_units) {
+               case E_DURATION_DAYS:
+                       if (trigger.u.rel_duration.days == user_interval
+                           && trigger.u.rel_duration.hours == 0
+                           && trigger.u.rel_duration.minutes == 0) {
+                               if (alarm_type)
+                                       *alarm_type = ALARM_USER_TIME;
+                               return FALSE;
+                       }
+                       break;
+
+               case E_DURATION_HOURS:
+                       if (trigger.u.rel_duration.days == 0
+                           && trigger.u.rel_duration.hours == user_interval
+                           && trigger.u.rel_duration.minutes == 0) {
+                               if (alarm_type)
+                                       *alarm_type = ALARM_USER_TIME;
+                               return FALSE;
+                       }
+                       break;
+
+               case E_DURATION_MINUTES:
+                       if (trigger.u.rel_duration.days == 0
+                           && trigger.u.rel_duration.hours == 0
+                           && trigger.u.rel_duration.minutes == user_interval) {
+                               if (alarm_type)
+                                       *alarm_type = ALARM_USER_TIME;
+                               return FALSE;
+                       }
+                       break;
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean
+ecep_reminders_is_custom_alarm_uid_list (ECalComponent *comp,
+                                        GList *alarms,
+                                        const gchar *old_summary,
+                                        EDurationType user_units,
+                                        gint user_interval,
+                                        gint *alarm_type)
+{
+       ECalComponentAlarm *ca;
+       gboolean result;
+
+       if (g_list_length (alarms) > 1)
+               return TRUE;
+
+       ca = e_cal_component_get_alarm (comp, alarms->data);
+       result = ecep_reminders_is_custom_alarm (ca, old_summary, user_units, user_interval, alarm_type);
+       e_cal_component_alarm_free (ca);
+
+       return result;
+}
+
+static void
+ecep_reminders_init_sensitable_combo_box (GtkComboBox *combo_box,
+                                         const gchar *first_item,
+                                         ...) G_GNUC_NULL_TERMINATED;
+
+static void
+ecep_reminders_init_sensitable_combo_box (GtkComboBox *combo_box,
+                                         const gchar *first_item,
+                                         ...)
+{
+       GtkCellRenderer *cell;
+       GtkCellLayout *cell_layout;
+       GtkListStore *store;
+       const gchar *item;
+       va_list va;
+
+       g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+
+       store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+       gtk_combo_box_set_model (combo_box, GTK_TREE_MODEL (store));
+       g_object_unref (store);
+
+       cell_layout = GTK_CELL_LAYOUT (combo_box);
+
+       gtk_cell_layout_clear (cell_layout);
+
+       cell = gtk_cell_renderer_text_new ();
+       gtk_cell_layout_pack_start (cell_layout, cell, TRUE);
+       gtk_cell_layout_set_attributes (
+               cell_layout, cell,
+               "text", 0,
+               "sensitive", 1,
+               NULL);
+
+       va_start (va, first_item);
+
+       item = first_item;
+       while (item) {
+               GtkTreeIter iter;
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (
+                       store, &iter,
+                       0, item,
+                       1, TRUE,
+                       -1);
+
+               item = va_arg (va, const gchar *);
+       }
+
+       va_end (va);
+}
+
+static void
+ecep_reminders_sensitize_relative_time_combo_items (GtkWidget *combobox,
+                                                   EClient *client,
+                                                   const gint *map,
+                                                   gint prop)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gboolean valid;
+       gboolean alarm_after_start;
+       gint ii;
+
+       alarm_after_start = !e_client_check_capability (client, CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START);
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combobox));
+       valid = gtk_tree_model_get_iter_first (model, &iter);
+
+       for (ii = 0; valid && map[ii] != -1; ii++) {
+               gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                               1, alarm_after_start ? TRUE : (map[ii] == prop ? FALSE : TRUE),
+                               -1);
+               valid = gtk_tree_model_iter_next (model, &iter);
+       }
+}
+
+static void
+ecep_reminders_sensitize_widgets_by_client (ECompEditorPageReminders *page_reminders,
+                                           ECompEditor *comp_editor,
+                                           EClient *target_client)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gboolean valid;
+       gint ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+       g_return_if_fail (E_IS_CAL_CLIENT (target_client));
+
+       /* Alarm types */
+       model = gtk_combo_box_get_model (GTK_COMBO_BOX (page_reminders->priv->kind_combo));
+       valid = gtk_tree_model_get_iter_first (model, &iter);
+
+       for (ii = 0; valid && action_map[ii] != -1; ii++) {
+               gtk_list_store_set (
+                       GTK_LIST_STORE (model), &iter,
+                       1, !e_client_check_capability (target_client, action_map_cap[ii]),
+                       -1);
+               valid = gtk_tree_model_iter_next (model, &iter);
+       }
+
+       ecep_reminders_sensitize_relative_time_combo_items (page_reminders->priv->relative_time_combo,
+               target_client, relative_map, AFTER);
+       ecep_reminders_sensitize_relative_time_combo_items (page_reminders->priv->relative_to_combo,
+               target_client, time_map, E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END);
+
+       /* If the client doesn't support set alarm description, disable the related widgets */
+       if (e_client_check_capability (target_client, CAL_STATIC_CAPABILITY_ALARM_DESCRIPTION)) {
+               gtk_widget_show (page_reminders->priv->custom_message_check);
+               gtk_widget_show (page_reminders->priv->custom_message_text_view);
+       } else {
+               gtk_widget_hide (page_reminders->priv->custom_message_check);
+               gtk_widget_hide (page_reminders->priv->custom_message_text_view);
+       }
+
+       /* Set a default address if possible */
+       ecep_reminders_set_alarm_email (page_reminders);
+
+       /* If we can repeat */
+       gtk_widget_set_sensitive (page_reminders->priv->repeat_check,
+               !e_client_check_capability (target_client, CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT));
+}
+
+static void
+ecep_reminders_sensitize_widgets (ECompEditorPage *page,
+                                 gboolean force_insensitive)
+{
+       ECompEditorPageReminders *page_reminders;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page));
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_reminders_parent_class)->sensitize_widgets (page, 
force_insensitive);
+
+       page_reminders = E_COMP_EDITOR_PAGE_REMINDERS (page);
+
+       gtk_widget_set_sensitive (page_reminders->priv->alarms_combo, !force_insensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->alarms_scrolled_window, !force_insensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->alarms_button_box, !force_insensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->alarm_setup_hbox, !force_insensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->repeat_setup_hbox, !force_insensitive);
+       gtk_widget_set_sensitive (page_reminders->priv->options_notebook, !force_insensitive);
+
+       if (!force_insensitive) {
+               ECompEditor *comp_editor;
+               ECalClient *target_client;
+
+               comp_editor = e_comp_editor_page_ref_editor (page);
+               target_client = e_comp_editor_get_target_client (comp_editor);
+
+               if (target_client)
+                       ecep_reminders_sensitize_widgets_by_client (page_reminders, comp_editor, E_CLIENT 
(target_client));
+
+               g_clear_object (&comp_editor);
+       }
+
+       ecep_reminders_sanitize_option_widgets (page_reminders);
+}
+
+static void
+ecep_reminders_fill_widgets (ECompEditorPage *page,
+                            icalcomponent *component)
+{
+       ECompEditorPageReminders *page_reminders;
+       ECalComponent *comp;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page));
+       g_return_if_fail (component != NULL);
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_reminders_parent_class)->fill_widgets (page, component);
+
+       page_reminders = E_COMP_EDITOR_PAGE_REMINDERS (page);
+
+       e_alarm_list_clear (page_reminders->priv->alarm_list);
+
+       if (!icalcomponent_get_first_component (component, ICAL_VALARM_COMPONENT)) {
+               e_dialog_combo_box_set (page_reminders->priv->alarms_combo, ALARM_NONE, 
page_reminders->priv->alarm_map);
+               return;
+       }
+
+       comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (component));
+       if (comp && e_cal_component_has_alarms (comp)) {
+               GList *alarms, *link;
+               gint alarm_type;
+
+               alarms = e_cal_component_get_alarm_uids (comp);
+
+               if (ecep_reminders_is_custom_alarm_uid_list (comp, alarms, icalcomponent_get_summary 
(component),
+                       page_reminders->priv->alarm_units, page_reminders->priv->alarm_interval, &alarm_type))
+                       alarm_type = ALARM_CUSTOM;
+
+               e_dialog_combo_box_set (page_reminders->priv->alarms_combo, alarm_type, 
page_reminders->priv->alarm_map);
+
+               e_alarm_list_clear (page_reminders->priv->alarm_list);
+
+               for (link = alarms; link; link = g_list_next (link)) {
+                       ECalComponentAlarm *ca;
+                       const gchar *uid = link->data;
+
+                       ca = e_cal_component_get_alarm (comp, uid);
+                       e_alarm_list_append (page_reminders->priv->alarm_list, NULL, ca);
+                       e_cal_component_alarm_free (ca);
+               }
+
+               cal_obj_uid_list_free (alarms);
+
+               if (e_dialog_combo_box_get (page_reminders->priv->alarms_combo, 
page_reminders->priv->alarm_map) == ALARM_CUSTOM) {
+                       GtkTreeSelection *selection;
+                       GtkTreeIter iter;
+
+                       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW 
(page_reminders->priv->alarms_tree_view));
+                       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (page_reminders->priv->alarm_list), 
&iter))
+                               gtk_tree_selection_select_iter (selection, &iter);
+               }
+       } else {
+               e_dialog_combo_box_set (page_reminders->priv->alarms_combo, ALARM_NONE, 
page_reminders->priv->alarm_map);
+       }
+
+       g_clear_object (&comp);
+}
+
+static gboolean
+ecep_reminders_fill_component (ECompEditorPage *page,
+                              icalcomponent *component)
+{
+       ECompEditorPageReminders *page_reminders;
+       ECalComponent *comp;
+       icalcomponent *changed_comp, *alarm;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gboolean valid_iter;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       if (!E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_reminders_parent_class)->fill_component (page, 
component))
+               return TRUE;
+
+       comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (component));
+       g_return_val_if_fail (comp != NULL, FALSE);
+
+       page_reminders = E_COMP_EDITOR_PAGE_REMINDERS (page);
+
+       e_cal_component_remove_all_alarms (comp);
+
+       model = GTK_TREE_MODEL (page_reminders->priv->alarm_list);
+
+       for (valid_iter = gtk_tree_model_get_iter_first (model, &iter);
+            valid_iter;
+            valid_iter = gtk_tree_model_iter_next (model, &iter)) {
+               ECalComponentAlarm *alarm, *alarm_copy;
+               icalcomponent *icalcomp;
+               icalproperty *prop;
+
+               alarm = (ECalComponentAlarm *) e_alarm_list_get_alarm (page_reminders->priv->alarm_list, 
&iter);
+               if (!alarm) {
+                       g_warning ("alarm is NULL\n");
+                       continue;
+               }
+
+               /* We set the description of the alarm if it's got
+                * the X-EVOLUTION-NEEDS-DESCRIPTION property.
+                */
+               icalcomp = e_cal_component_alarm_get_icalcomponent (alarm);
+               for (prop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
+                    prop;
+                    prop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
+                       const gchar *x_name;
+
+                       x_name = icalproperty_get_x_name (prop);
+                       if (g_str_equal (x_name, X_EVOLUTION_NEEDS_DESCRIPTION)) {
+                               ECalComponentText summary;
+
+                               e_cal_component_get_summary (comp, &summary);
+                               e_cal_component_alarm_set_description (alarm, &summary);
+
+                               icalcomponent_remove_property (icalcomp, prop);
+                               icalproperty_free (prop);
+                               break;
+                       }
+               }
+
+               /* We clone the alarm to maintain the invariant that the alarm
+                * structures in the list did *not* come from the component.
+                */
+
+               alarm_copy = e_cal_component_alarm_clone (alarm);
+               e_cal_component_add_alarm (comp, alarm_copy);
+               e_cal_component_alarm_free (alarm_copy);
+       }
+
+       while (alarm = icalcomponent_get_first_component (component, ICAL_VALARM_COMPONENT), alarm) {
+               icalcomponent_remove_component (component, alarm);
+               icalcomponent_free (alarm);
+       }
+
+       changed_comp = e_cal_component_get_icalcomponent (comp);
+       if (changed_comp) {
+               /* Move all VALARM components into the right 'component' */
+               while (alarm = icalcomponent_get_first_component (changed_comp, ICAL_VALARM_COMPONENT), 
alarm) {
+                       icalcomponent_remove_component (changed_comp, alarm);
+                       icalcomponent_add_component (component, alarm);
+               }
+       } else {
+               g_warn_if_reached ();
+       }
+
+       g_clear_object (&comp);
+
+       return TRUE;
+}
+
+static void
+ecep_reminders_select_page_cb (GtkAction *action,
+                              ECompEditorPage *page)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page));
+
+       e_comp_editor_page_select (page);
+}
+
+static void
+ecep_reminders_setup_ui (ECompEditorPageReminders *page_reminders)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='tabs'>"
+               "        <menuitem action='page-reminders'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "  <toolbar name='main-toolbar'>"
+               "    <placeholder name='content'>\n"
+               "      <toolitem action='page-reminders'/>\n"
+               "    </placeholder>"
+               "  </toolbar>"
+               "</ui>";
+
+       const GtkActionEntry options_actions[] = {
+               { "page-reminders",
+                 "appointment-soon",
+                 N_("_Reminders"),
+                 NULL,
+                 N_("Set or unset reminders"),
+                 G_CALLBACK (ecep_reminders_select_page_cb) }
+       };
+
+       ECompEditor *comp_editor;
+       GtkUIManager *ui_manager;
+       GtkActionGroup *action_group;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_REMINDERS (page_reminders));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_reminders));
+       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,
+               options_actions, G_N_ELEMENTS (options_actions), page_reminders);
+
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+       g_clear_object (&comp_editor);
+
+       if (error) {
+               g_critical ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+static void
+ecep_reminders_constructed (GObject *object)
+{
+       ECompEditorPageReminders *page_reminders;
+       GtkWidget *widget, *container, *label, *option_grid;
+       GtkComboBoxText *text_combo;
+       GtkTreeViewColumn *column;
+       GtkTextBuffer *text_buffer;
+       GtkCellRenderer *cell_renderer;
+       PangoAttrList *bold;
+       GtkGrid *grid;
+       ECompEditor *comp_editor;
+       EFocusTracker *focus_tracker;
+       gchar *combo_label, *config_dir;
+
+       G_OBJECT_CLASS (e_comp_editor_page_reminders_parent_class)->constructed (object);
+
+       page_reminders = E_COMP_EDITOR_PAGE_REMINDERS (object);
+       grid = GTK_GRID (page_reminders);
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_reminders));
+       focus_tracker = e_comp_editor_get_focus_tracker (comp_editor);
+
+       bold = pango_attr_list_new ();
+       pango_attr_list_insert (bold, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+
+       widget = gtk_label_new (_("Reminders"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "attributes", bold,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 0, 2, 1);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 1, 2, 1);
+
+       container = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Reminder"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       label = widget;
+
+       widget = gtk_combo_box_text_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->alarms_combo = widget;
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), page_reminders->priv->alarms_combo);
+
+       /* Add the user defined time if necessary */
+       page_reminders->priv->alarm_interval = calendar_config_get_default_reminder_interval ();
+       page_reminders->priv->alarm_units = calendar_config_get_default_reminder_units ();
+
+       combo_label = NULL;
+
+       switch (page_reminders->priv->alarm_units) {
+       case E_DURATION_DAYS:
+               if (page_reminders->priv->alarm_interval != 1) {
+                       combo_label = g_strdup_printf (ngettext ("%d day before", "%d days before",
+                               page_reminders->priv->alarm_interval), page_reminders->priv->alarm_interval);
+               }
+               break;
+
+       case E_DURATION_HOURS:
+               if (page_reminders->priv->alarm_interval != 1) {
+                       combo_label = g_strdup_printf (ngettext ("%d hour before", "%d hours before",
+                               page_reminders->priv->alarm_interval), page_reminders->priv->alarm_interval);
+               }
+               break;
+
+       case E_DURATION_MINUTES:
+               if (page_reminders->priv->alarm_interval != 15) {
+                       combo_label = g_strdup_printf (ngettext ("%d minute before", "%d minutes before",
+                               page_reminders->priv->alarm_interval), page_reminders->priv->alarm_interval);
+               }
+               break;
+       }
+
+       text_combo = GTK_COMBO_BOX_TEXT (widget);
+       /* Translators: "None" for "No reminder set" */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "None"));
+        /* Translators: Predefined reminder's description */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "15 minutes before"));
+        /* Translators: Predefined reminder's description */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "1 hour before"));
+        /* Translators: Predefined reminder's description */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "1 day before"));
+
+       if (combo_label) {
+               gtk_combo_box_text_append_text (text_combo, combo_label);
+               g_free (combo_label);
+
+               page_reminders->priv->alarm_map = alarm_map_with_user_time;
+       } else {
+               page_reminders->priv->alarm_map = alarm_map_without_user_time;
+       }
+
+       /* Translators: "Custom" for "Custom reminder set" */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "Custom"));
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), 0);
+
+       g_signal_connect (page_reminders->priv->alarms_combo, "changed",
+               G_CALLBACK (ecep_reminders_alarms_combo_changed_cb), page_reminders);
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_FILL,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "shadow-type", GTK_SHADOW_IN,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+
+       page_reminders->priv->alarms_scrolled_window = widget;
+       container = widget;
+
+       page_reminders->priv->alarm_list = e_alarm_list_new ();
+
+       widget = gtk_tree_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "model", page_reminders->priv->alarm_list,
+               "headers-visible", FALSE,
+               NULL);
+       gtk_widget_show (widget);
+
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       page_reminders->priv->alarms_tree_view = widget;
+
+       column = gtk_tree_view_column_new ();
+       gtk_tree_view_column_set_title (column, "Action/Trigger");
+       cell_renderer = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+       gtk_tree_view_column_add_attribute (column, cell_renderer, "text", E_ALARM_LIST_COLUMN_DESCRIPTION);
+       gtk_tree_view_append_column (GTK_TREE_VIEW (page_reminders->priv->alarms_tree_view), column);
+
+       g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW 
(page_reminders->priv->alarms_tree_view)),
+               "changed", G_CALLBACK (ecep_reminders_alarms_selection_changed_cb), page_reminders);
+
+       widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+       page_reminders->priv->alarms_button_box = widget;
+
+       widget = gtk_button_new_with_mnemonic (_("A_dd"));
+       gtk_box_pack_start (GTK_BOX (page_reminders->priv->alarms_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       page_reminders->priv->alarms_add_button = widget;
+
+       g_signal_connect (page_reminders->priv->alarms_add_button, "clicked",
+               G_CALLBACK (ecep_reminders_alarms_add_clicked_cb), page_reminders);
+
+       widget = gtk_button_new_with_mnemonic (_("Re_move"));
+       gtk_box_pack_start (GTK_BOX (page_reminders->priv->alarms_button_box), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       page_reminders->priv->alarms_remove_button = widget;
+
+       g_signal_connect (page_reminders->priv->alarms_remove_button, "clicked",
+               G_CALLBACK (ecep_reminders_alarms_remove_clicked_cb), page_reminders);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 3, 2, 1);
+
+       page_reminders->priv->alarm_setup_hbox = widget;
+       container = widget;
+
+       widget = gtk_combo_box_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->kind_combo = widget;
+
+       ecep_reminders_init_sensitable_combo_box (GTK_COMBO_BOX (widget),
+               /* Translators: Part of: [ Pop up an alert ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+               C_("cal-reminders", "Pop up an alert"),
+               /* Translators: Part of: [ Play a sound ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+               C_("cal-reminders", "Play a sound"),
+               /* Translators: Part of: [ Run a program ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+               C_("cal-reminders", "Run a program"),
+               /* Translators: Part of: [ Send an email ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+               C_("cal-reminders", "Send an email"),
+               NULL);
+
+       g_signal_connect (page_reminders->priv->kind_combo, "changed",
+               G_CALLBACK (ecep_reminders_kind_combo_changed_cb), page_reminders);
+
+       widget = gtk_spin_button_new_with_range (0, 999, 1);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "digits", 0,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->time_spin = widget;
+
+       widget = gtk_combo_box_text_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->unit_combo = widget;
+
+       text_combo = GTK_COMBO_BOX_TEXT (widget);
+       /* Translators: Part of: [ Pop up an alert ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "minute(s)"));
+       /* Translators: Part of: [ Pop up an alert ] [ x ] [ hour(s) ] [ before ] [ start ]*/
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "hour(s)"));
+       /* Translators: Part of: [ Pop up an alert ] [ x ] [ day(s) ] [ before ] [ start ]*/
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "day(s)"));
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), 0);
+
+       widget = gtk_combo_box_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->relative_time_combo = widget;
+
+       ecep_reminders_init_sensitable_combo_box (GTK_COMBO_BOX (widget),
+               /* Translators: Part of: [ Pop up an alert ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+               C_("cal-reminders", "before"),
+               /* Translators: Part of: [ Pop up an alert ] [ x ] [ minute(s) ] [ after ] [ start ]*/
+               C_("cal-reminders", "after"),
+               NULL);
+
+       widget = gtk_combo_box_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->relative_to_combo = widget;
+
+       ecep_reminders_init_sensitable_combo_box (GTK_COMBO_BOX (widget),
+               /* Translators: Part of: [ Pop up an alert ] [ x ] [ minute(s) ] [ before ] [ start ]*/
+               C_("cal-reminders", "start"),
+               /* Translators: Part of: [ Pop up an alert ] [ x ] [ minute(s) ] [ before ] [ end ]*/
+               C_("cal-reminders", "end"),
+               NULL);
+
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "margin-bottom", 6,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 4, 2, 1);
+
+       page_reminders->priv->repeat_setup_hbox = widget;
+       container = widget;
+
+       /* Translators: Part of: Repeat the reminder [ x ] extra times every [ y ] [ minutes ] */
+       widget = gtk_check_button_new_with_mnemonic (_("Re_peat the reminder"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+
+       page_reminders->priv->repeat_check = widget;
+
+       widget = gtk_spin_button_new_with_range (1, 999, 1);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "digits", 0,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->repeat_times_spin = widget;
+
+       /* Translators: Part of: Repeat the reminder [ x ] extra times every [ y ] [ minutes ] */
+       widget = gtk_label_new (C_("cal-reminders", "extra times every"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+
+       page_reminders->priv->repeat_every_label = widget;
+
+       widget = gtk_spin_button_new_with_range (1, 999, 1);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               "digits", 0,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->repeat_every_spin = widget;
+
+       widget = gtk_combo_box_text_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       page_reminders->priv->repeat_unit_combo = widget;
+
+       text_combo = GTK_COMBO_BOX_TEXT (widget);
+       /* Translators: Part of: Repeat the reminder [ x ] extra times every [ y ] [ minutes ] */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "minutes"));
+       /* Translators: Part of: Repeat the reminder [ x ] extra times every [ y ] [ hours ] */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "hours"));
+       /* Translators: Part of: Repeat the reminder [ x ] extra times every [ y ] [ days ] */
+       gtk_combo_box_text_append_text (text_combo, C_("cal-reminders", "days"));
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (text_combo), 0);
+
+       widget = gtk_label_new (_("Options"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "attributes", bold,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 5, 2, 1);
+
+       page_reminders->priv->options_label = widget;
+
+       widget = gtk_notebook_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               #if GTK_CHECK_VERSION(3, 12, 0)
+               "margin-start", 12,
+               #else
+               "margin-left", 12,
+               #endif
+               "show-tabs", FALSE,
+               "show-border", FALSE,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (grid, widget, 0, 6, 2, 1);
+
+       page_reminders->priv->options_notebook = widget;
+
+       /* Custom message page */
+
+       option_grid = gtk_grid_new ();
+       gtk_widget_show (option_grid);
+
+       widget = gtk_check_button_new_with_mnemonic (C_("cal-reminders", "Custom _message"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 0, 1, 1);
+       page_reminders->priv->custom_message_check = widget;
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "shadow-type", GTK_SHADOW_IN,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 1, 1, 1);
+
+       container = widget;
+
+       widget = gtk_text_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       page_reminders->priv->custom_message_text_view = widget;
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (page_reminders->priv->options_notebook), option_grid, NULL);
+
+       /* Custom sound page */
+
+       option_grid = gtk_grid_new ();
+       gtk_widget_show (option_grid);
+
+       widget = gtk_check_button_new_with_mnemonic (C_("cal-reminders", "Custom reminder _sound"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 0, 1, 1);
+       page_reminders->priv->custom_sound_check = widget;
+
+       widget = gtk_file_chooser_button_new (_("Select a sound file"), GTK_FILE_CHOOSER_ACTION_OPEN);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 1, 1, 1);
+
+       config_dir = calendar_config_get_dir_path ();
+       if (config_dir && *config_dir)
+               gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), config_dir);
+       g_free (config_dir);
+
+       page_reminders->priv->custom_sound_chooser = widget;
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (page_reminders->priv->options_notebook), option_grid, NULL);
+
+       /* Custom program page */
+
+       option_grid = gtk_grid_new ();
+       g_object_set (G_OBJECT (option_grid),
+               "column-spacing", 4,
+               "row-spacing", 4,
+               NULL);
+       gtk_widget_show (option_grid);
+
+       widget = gtk_label_new_with_mnemonic (_("_Program:"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 0, 1, 1);
+
+       label = widget;
+
+       widget = gtk_entry_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 1, 0, 1, 1);
+       page_reminders->priv->custom_app_path_entry = widget;
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+
+       widget = gtk_label_new_with_mnemonic (_("_Arguments:"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 1, 1, 1);
+
+       label = widget;
+
+       widget = gtk_entry_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 1, 1, 1, 1);
+       page_reminders->priv->custom_app_args_entry = widget;
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (page_reminders->priv->options_notebook), option_grid, NULL);
+
+       /* Custom email page */
+
+       option_grid = gtk_grid_new ();
+       gtk_widget_show (option_grid);
+
+       widget = gtk_button_new_with_mnemonic (_("_Send To:"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 0, 1, 1);
+
+       page_reminders->priv->custom_email_button = widget;
+
+       g_signal_connect (page_reminders->priv->custom_email_button, "clicked",
+               G_CALLBACK (ecep_reminders_send_to_clicked_cb), page_reminders);
+
+       /* page_reminders->priv->custom_email_entry is initialized on demand */
+
+       widget = gtk_check_button_new_with_mnemonic (C_("cal-reminders", "Custom _message"));
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 1, 2, 1);
+       page_reminders->priv->custom_email_message_check = widget;
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "shadow-type", GTK_SHADOW_IN,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (option_grid), widget, 0, 2, 2, 1);
+
+       container = widget;
+
+       widget = gtk_text_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_container_add (GTK_CONTAINER (container), widget);
+       page_reminders->priv->custom_email_message_text_view = widget;
+
+       gtk_notebook_append_page (GTK_NOTEBOOK (page_reminders->priv->options_notebook), option_grid, NULL);
+
+       pango_attr_list_unref (bold);
+
+       e_widget_undo_attach (page_reminders->priv->custom_message_text_view, focus_tracker);
+       e_widget_undo_attach (page_reminders->priv->custom_email_message_text_view, focus_tracker);
+
+       e_spell_text_view_attach (GTK_TEXT_VIEW (page_reminders->priv->custom_message_text_view));
+       e_spell_text_view_attach (GTK_TEXT_VIEW (page_reminders->priv->custom_email_message_text_view));
+
+       g_clear_object (&comp_editor);
+
+       g_signal_connect_swapped (page_reminders->priv->repeat_check, "toggled",
+               G_CALLBACK (ecep_reminders_sanitize_option_widgets), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_message_check, "toggled",
+               G_CALLBACK (ecep_reminders_sanitize_option_widgets), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_sound_check, "toggled",
+               G_CALLBACK (ecep_reminders_sanitize_option_widgets), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_email_message_check, "toggled",
+               G_CALLBACK (ecep_reminders_sanitize_option_widgets), page_reminders);
+
+       g_signal_connect_swapped (page_reminders->priv->kind_combo, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->time_spin, "value-changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->unit_combo, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->relative_time_combo, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->relative_to_combo, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->repeat_check, "toggled",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->repeat_times_spin, "value-changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->repeat_every_spin, "value-changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->repeat_unit_combo, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_message_check, "toggled",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW 
(page_reminders->priv->custom_message_text_view));
+       g_signal_connect_swapped (text_buffer, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_sound_check, "toggled",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_sound_chooser, "file-set",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_app_path_entry, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_app_args_entry, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       g_signal_connect_swapped (page_reminders->priv->custom_email_message_check, "toggled",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+       text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW 
(page_reminders->priv->custom_email_message_text_view));
+       g_signal_connect_swapped (text_buffer, "changed",
+               G_CALLBACK (ecep_reminders_widgets_to_selected), page_reminders);
+
+       ecep_reminders_setup_ui (page_reminders);
+}
+
+static void
+ecep_reminders_dispose (GObject *object)
+{
+       ECompEditorPageReminders *page_reminders;
+
+       page_reminders = E_COMP_EDITOR_PAGE_REMINDERS (object);
+
+       if (page_reminders->priv->name_selector)
+               e_name_selector_cancel_loading (page_reminders->priv->name_selector);
+
+       g_clear_object (&page_reminders->priv->alarm_list);
+       g_clear_object (&page_reminders->priv->name_selector);
+
+       G_OBJECT_CLASS (e_comp_editor_page_reminders_parent_class)->dispose (object);
+}
+
+static void
+e_comp_editor_page_reminders_init (ECompEditorPageReminders *page_reminders)
+{
+       page_reminders->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_reminders,
+               E_TYPE_COMP_EDITOR_PAGE_REMINDERS,
+               ECompEditorPageRemindersPrivate);
+}
+
+static void
+e_comp_editor_page_reminders_class_init (ECompEditorPageRemindersClass *klass)
+{
+       ECompEditorPageClass *page_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPageRemindersPrivate));
+
+       page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
+       page_class->sensitize_widgets = ecep_reminders_sensitize_widgets;
+       page_class->fill_widgets = ecep_reminders_fill_widgets;
+       page_class->fill_component = ecep_reminders_fill_component;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = ecep_reminders_constructed;
+       object_class->dispose = ecep_reminders_dispose;
+}
+
+ECompEditorPage *
+e_comp_editor_page_reminders_new (ECompEditor *editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
+
+       return g_object_new (E_TYPE_COMP_EDITOR_PAGE_REMINDERS,
+               "editor", editor,
+               NULL);
+}
diff --git a/calendar/gui/e-comp-editor-page-reminders.h b/calendar/gui/e-comp-editor-page-reminders.h
new file mode 100644
index 0000000..c5d7c36
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-reminders.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PAGE_REMINDERS_H
+#define E_COMP_EDITOR_PAGE_REMINDERS_H
+
+#include <calendar/gui/e-comp-editor.h>
+#include <calendar/gui/e-comp-editor-page.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PAGE_REMINDERS \
+       (e_comp_editor_page_reminders_get_type ())
+#define E_COMP_EDITOR_PAGE_REMINDERS(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_REMINDERS, ECompEditorPageReminders))
+#define E_COMP_EDITOR_PAGE_REMINDERS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_REMINDERS, ECompEditorPageRemindersClass))
+#define E_IS_COMP_EDITOR_PAGE_REMINDERS(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_REMINDERS))
+#define E_IS_COMP_EDITOR_PAGE_REMINDERS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_REMINDERS))
+#define E_COMP_EDITOR_PAGE_REMINDERS_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_REMINDERS, ECompEditorPageRemindersClass))
+
+typedef struct _ECompEditorPageReminders ECompEditorPageReminders;
+typedef struct _ECompEditorPageRemindersClass ECompEditorPageRemindersClass;
+typedef struct _ECompEditorPageRemindersPrivate ECompEditorPageRemindersPrivate;
+
+struct _ECompEditorPageReminders {
+       ECompEditorPage parent;
+
+       ECompEditorPageRemindersPrivate *priv;
+};
+
+struct _ECompEditorPageRemindersClass {
+       ECompEditorPageClass parent_class;
+};
+
+GType          e_comp_editor_page_reminders_get_type   (void) G_GNUC_CONST;
+ECompEditorPage *
+               e_comp_editor_page_reminders_new        (ECompEditor *editor);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PAGE_REMINDERS_H */
diff --git a/calendar/gui/e-comp-editor-page-schedule.c b/calendar/gui/e-comp-editor-page-schedule.c
new file mode 100644
index 0000000..1167c00
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-schedule.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "e-comp-editor-page-schedule.h"
+
+struct _ECompEditorPageSchedulePrivate {
+       EMeetingStore *store;
+       EMeetingTimeSelector *selector;
+};
+
+enum {
+       PROP_0,
+       PROP_STORE
+};
+
+G_DEFINE_TYPE (ECompEditorPageSchedule, e_comp_editor_page_schedule, E_TYPE_COMP_EDITOR_PAGE)
+
+static void
+ecep_schedule_get_work_day_range_for (GSettings *settings,
+                                     gint weekday,
+                                     gint *start_hour,
+                                     gint *start_minute,
+                                     gint *end_hour,
+                                     gint *end_minute)
+{
+       gint start_adept = -1, end_adept = -1;
+       const gchar *start_key = NULL, *end_key = NULL;
+
+       g_return_if_fail (G_IS_SETTINGS (settings));
+       g_return_if_fail (start_hour != NULL);
+       g_return_if_fail (start_minute != NULL);
+       g_return_if_fail (end_hour != NULL);
+       g_return_if_fail (end_minute != NULL);
+
+       switch (weekday) {
+               case G_DATE_MONDAY:
+                       start_key = "day-start-mon";
+                       end_key = "day-end-mon";
+                       break;
+               case G_DATE_TUESDAY:
+                       start_key = "day-start-tue";
+                       end_key = "day-end-tue";
+                       break;
+               case G_DATE_WEDNESDAY:
+                       start_key = "day-start-wed";
+                       end_key = "day-end-wed";
+                       break;
+               case G_DATE_THURSDAY:
+                       start_key = "day-start-thu";
+                       end_key = "day-end-thu";
+                       break;
+               case G_DATE_FRIDAY:
+                       start_key = "day-start-fri";
+                       end_key = "day-end-fri";
+                       break;
+               case G_DATE_SATURDAY:
+                       start_key = "day-start-sat";
+                       end_key = "day-end-sat";
+                       break;
+               case G_DATE_SUNDAY:
+                       start_key = "day-start-sun";
+                       end_key = "day-end-sun";
+                       break;
+               default:
+                       break;
+       }
+
+       if (start_key && end_key) {
+               start_adept = g_settings_get_int (settings, start_key);
+               end_adept = g_settings_get_int (settings, end_key);
+       }
+
+       if (start_adept > 0 && (start_adept / 100) >= 0 && (start_adept / 100) <= 23 &&
+           (start_adept % 100) >= 0 && (start_adept % 100) <= 59) {
+               *start_hour = start_adept / 100;
+               *start_minute = start_adept % 100;
+       } else {
+               *start_hour = g_settings_get_int (settings, "day-start-hour");
+               *start_minute = g_settings_get_int (settings, "day-start-minute");
+       }
+
+       if (end_adept > 0 && (end_adept / 100) >= 0 && (end_adept / 100) <= 23 &&
+           (end_adept % 100) >= 0 && (end_adept % 100) <= 59) {
+               *end_hour = end_adept / 100;
+               *end_minute = end_adept % 100;
+       } else {
+               *end_hour = g_settings_get_int (settings, "day-end-hour");
+               *end_minute = g_settings_get_int (settings, "day-end-minute");
+       }
+}
+
+static void
+ecep_schedule_editor_times_changed_cb (ECompEditor *comp_editor,
+                                      ECompEditorPageSchedule *page_schedule)
+{
+       ECompEditorPropertyPartDatetime *dtstart, *dtend;
+       ECompEditorPropertyPart *dtstart_part = NULL, *dtend_part = NULL;
+       EDateEdit *start_date_edit, *end_date_edit;
+       struct icaltimetype start_tt, end_tt;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
+       g_return_if_fail (page_schedule->priv->selector != NULL);
+
+       e_comp_editor_get_time_parts (comp_editor, &dtstart_part, &dtend_part);
+
+       if (!dtstart_part || !dtend_part)
+               return;
+
+       dtstart = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part);
+       dtend = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part);
+
+       start_tt = e_comp_editor_property_part_datetime_get_value (dtstart);
+       end_tt = e_comp_editor_property_part_datetime_get_value (dtend);
+
+       /* For All Day Events, if DTEND is after DTSTART, we subtract 1 day from it. */
+       if (start_tt.is_date && end_tt.is_date &&
+           icaltime_compare_date_only (end_tt, start_tt) > 0)
+               icaltime_adjust (&end_tt, -1, 0, 0, 0);
+
+       e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_schedule), TRUE);
+
+       start_date_edit = E_DATE_EDIT (page_schedule->priv->selector->start_date_edit);
+       end_date_edit = E_DATE_EDIT (page_schedule->priv->selector->end_date_edit);
+
+       e_date_edit_set_date (start_date_edit, start_tt.year, start_tt.month, start_tt.day);
+       e_date_edit_set_time_of_day (start_date_edit, start_tt.hour, start_tt.minute);
+
+       e_date_edit_set_date (end_date_edit, end_tt.year, end_tt.month, end_tt.day);
+       e_date_edit_set_time_of_day (end_date_edit, end_tt.hour, end_tt.minute);
+
+       e_comp_editor_page_set_updating (E_COMP_EDITOR_PAGE (page_schedule), FALSE);
+}
+
+static void
+ecep_schedule_set_time_to_editor (ECompEditorPageSchedule *page_schedule)
+{
+       EMeetingTimeSelector *selector;
+       ECompEditorPropertyPartDatetime *dtstart, *dtend;
+       ECompEditorPropertyPart *dtstart_part = NULL, *dtend_part = NULL;
+       ECompEditor *comp_editor;
+       struct icaltimetype start_tt, end_tt;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
+       g_return_if_fail (E_IS_MEETING_TIME_SELECTOR (page_schedule->priv->selector));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
+       if (comp_editor)
+               e_comp_editor_get_time_parts (comp_editor, &dtstart_part, &dtend_part);
+
+       if (!dtstart_part || !dtend_part) {
+               g_clear_object (&comp_editor);
+               return;
+       }
+
+       selector = page_schedule->priv->selector;
+       dtstart = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part);
+       dtend = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part);
+
+       start_tt = e_comp_editor_property_part_datetime_get_value (dtstart);
+       end_tt = e_comp_editor_property_part_datetime_get_value (dtend);
+
+       e_date_edit_get_date (
+               E_DATE_EDIT (selector->start_date_edit),
+               &start_tt.year,
+               &start_tt.month,
+               &start_tt.day);
+       e_date_edit_get_time_of_day (
+               E_DATE_EDIT (selector->start_date_edit),
+               &start_tt.hour,
+               &start_tt.minute);
+       e_date_edit_get_date (
+               E_DATE_EDIT (selector->end_date_edit),
+               &end_tt.year,
+               &end_tt.month,
+               &end_tt.day);
+       e_date_edit_get_time_of_day (
+               E_DATE_EDIT (selector->end_date_edit),
+               &end_tt.hour,
+               &end_tt.minute);
+
+       if (!e_date_edit_get_show_time (E_DATE_EDIT (selector->start_date_edit))) {
+               /* For All-Day Events, we set the timezone to NULL, and add 1 day to DTEND. */
+               start_tt.is_date = TRUE;
+               start_tt.zone = NULL;
+               end_tt.is_date = TRUE;
+               end_tt.zone = NULL;
+
+               icaltime_adjust (&end_tt, 1, 0, 0, 0);
+       } else {
+               start_tt.is_date = FALSE;
+               end_tt.is_date = FALSE;
+       }
+
+       e_comp_editor_property_part_datetime_set_value (dtstart, start_tt);
+       e_comp_editor_property_part_datetime_set_value (dtend, end_tt);
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+ecep_schedule_selector_changed_cb (EMeetingTimeSelector *selector,
+                                  ECompEditorPageSchedule *page_schedule)
+{
+       ECompEditorPage *page;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
+       g_return_if_fail (page_schedule->priv->selector == selector);
+
+       page = E_COMP_EDITOR_PAGE (page_schedule);
+
+       if (e_comp_editor_page_get_updating (page))
+               return;
+
+       e_comp_editor_page_set_updating (page, TRUE);
+
+       ecep_schedule_set_time_to_editor (page_schedule);
+
+       e_comp_editor_page_set_updating (page, FALSE);
+       e_comp_editor_page_emit_changed (page);
+}
+
+static void
+ecep_schedule_sensitize_widgets (ECompEditorPage *page,
+                                gboolean force_insensitive)
+{
+       ECompEditorPageSchedule *page_schedule;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_schedule_parent_class)->sensitize_widgets (page, 
force_insensitive);
+
+       page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (page);
+
+       e_meeting_time_selector_set_read_only (page_schedule->priv->selector, force_insensitive);
+}
+
+static void
+ecep_schedule_fill_widgets (ECompEditorPage *page,
+                           icalcomponent *component)
+{
+       ECompEditorPageSchedule *page_schedule;
+       ECompEditorPropertyPartDatetime *dtstart, *dtend;
+       ECompEditorPropertyPart *dtstart_part = NULL, *dtend_part = NULL;
+       ECompEditor *comp_editor;
+       EMeetingTimeSelector *selector;
+       struct icaltimetype start_tt, end_tt;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
+       g_return_if_fail (component != NULL);
+
+       E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_schedule_parent_class)->fill_widgets (page, component);
+
+       page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (page);
+
+       /* dtstart/dtend parts should be already populated, thus
+          get values from them, instead of from the component */
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
+       g_return_if_fail (E_IS_MEETING_TIME_SELECTOR (page_schedule->priv->selector));
+
+       comp_editor = e_comp_editor_page_ref_editor (page);
+       if (comp_editor)
+               e_comp_editor_get_time_parts (comp_editor, &dtstart_part, &dtend_part);
+
+       if (!dtstart_part || !dtend_part) {
+               g_clear_object (&comp_editor);
+               return;
+       }
+
+       selector = page_schedule->priv->selector;
+       dtstart = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part);
+       dtend = E_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part);
+
+       start_tt = e_comp_editor_property_part_datetime_get_value (dtstart);
+       end_tt = e_comp_editor_property_part_datetime_get_value (dtend);
+
+       if (start_tt.is_date) {
+               /* For All-Day Events, we set the timezone to NULL, and add 1 day to DTEND. */
+               start_tt.is_date = TRUE;
+               start_tt.zone = NULL;
+               end_tt.is_date = TRUE;
+               end_tt.zone = NULL;
+
+               icaltime_adjust (&end_tt, 1, 0, 0, 0);
+       } else {
+               start_tt.is_date = FALSE;
+               end_tt.is_date = FALSE;
+       }
+
+       e_comp_editor_page_set_updating (page, TRUE);
+
+       e_date_edit_set_date (
+               E_DATE_EDIT (selector->start_date_edit),
+               start_tt.year,
+               start_tt.month,
+               start_tt.day);
+       e_date_edit_set_time_of_day (
+               E_DATE_EDIT (selector->start_date_edit),
+               start_tt.hour,
+               start_tt.minute);
+       e_date_edit_set_date (
+               E_DATE_EDIT (selector->end_date_edit),
+               end_tt.year,
+               end_tt.month,
+               end_tt.day);
+       e_date_edit_set_time_of_day (
+               E_DATE_EDIT (selector->end_date_edit),
+               end_tt.hour,
+               end_tt.minute);
+
+       e_comp_editor_page_set_updating (page, FALSE);
+
+       g_clear_object (&comp_editor);
+}
+
+static gboolean
+ecep_schedule_fill_component (ECompEditorPage *page,
+                             icalcomponent *component)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       return E_COMP_EDITOR_PAGE_CLASS (e_comp_editor_page_schedule_parent_class)->fill_component (page, 
component);
+}
+
+static void
+e_comp_editor_page_schedule_set_store (ECompEditorPageSchedule *page_schedule,
+                                      EMeetingStore *store)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
+       g_return_if_fail (E_IS_MEETING_STORE (store));
+       g_return_if_fail (page_schedule->priv->store == NULL);
+
+       page_schedule->priv->store = g_object_ref (store);
+}
+
+static void
+e_comp_editor_page_schedule_set_property (GObject *object,
+                                         guint property_id,
+                                         const GValue *value,
+                                         GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_STORE:
+                       e_comp_editor_page_schedule_set_store (
+                               E_COMP_EDITOR_PAGE_SCHEDULE (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_page_schedule_get_property (GObject *object,
+                                         guint property_id,
+                                         GValue *value,
+                                         GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_STORE:
+                       g_value_set_object (
+                               value,
+                               e_comp_editor_page_schedule_get_store (
+                               E_COMP_EDITOR_PAGE_SCHEDULE (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecep_schedule_select_page_cb (GtkAction *action,
+                             ECompEditorPage *page)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page));
+
+       e_comp_editor_page_select (page);
+}
+
+static void
+ecep_schedule_setup_ui (ECompEditorPageSchedule *page_schedule)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='tabs'>"
+               "        <menuitem action='page-schedule'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "  <toolbar name='main-toolbar'>"
+               "    <placeholder name='after-content'>\n"
+               "      <toolitem action='page-schedule'/>\n"
+               "    </placeholder>"
+               "  </toolbar>"
+               "</ui>";
+
+       const GtkActionEntry options_actions[] = {
+               { "page-schedule",
+                 "query-free-busy",
+                 N_("_Schedule"),
+                 NULL,
+                 N_("Query free / busy information for the attendees"),
+                 G_CALLBACK (ecep_schedule_select_page_cb) }
+       };
+
+       ECompEditor *comp_editor;
+       GtkUIManager *ui_manager;
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule));
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
+       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,
+               options_actions, G_N_ELEMENTS (options_actions), page_schedule);
+
+       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);
+       }
+
+       action = e_comp_editor_get_action (comp_editor, "page-schedule");
+       e_binding_bind_property (
+               page_schedule, "visible",
+               action, "visible",
+               G_BINDING_SYNC_CREATE);
+
+       g_clear_object (&comp_editor);
+}
+
+static void
+e_comp_editor_page_schedule_constructed (GObject *object)
+{
+       ECompEditorPageSchedule *page_schedule;
+       ECompEditor *comp_editor;
+       GSettings *settings;
+       GtkWidget *widget;
+       gint weekday;
+
+       G_OBJECT_CLASS (e_comp_editor_page_schedule_parent_class)->constructed (object);
+
+       page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (object);
+
+       g_return_if_fail (page_schedule->priv->store != NULL);
+
+       widget = e_meeting_time_selector_new (page_schedule->priv->store);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+       gtk_grid_attach (GTK_GRID (page_schedule), widget, 0, 0, 1, 1);
+
+       page_schedule->priv->selector = E_MEETING_TIME_SELECTOR (widget);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+
+       for (weekday = G_DATE_BAD_WEEKDAY; weekday <= G_DATE_SUNDAY; weekday++) {
+               gint start_hour, start_minute, end_hour, end_minute;
+
+               ecep_schedule_get_work_day_range_for (settings, weekday,
+                       &start_hour, &start_minute, &end_hour, &end_minute);
+
+               e_meeting_time_selector_set_working_hours (page_schedule->priv->selector,
+                       weekday, start_hour, start_minute, end_hour, end_minute);
+       }
+
+       g_clear_object (&settings);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
+       if (comp_editor) {
+               g_signal_connect (comp_editor, "times-changed",
+                       G_CALLBACK (ecep_schedule_editor_times_changed_cb), page_schedule);
+       }
+
+       g_clear_object (&comp_editor);
+
+       g_signal_connect (page_schedule->priv->selector, "changed",
+               G_CALLBACK (ecep_schedule_selector_changed_cb), page_schedule);
+
+       ecep_schedule_setup_ui (page_schedule);
+}
+
+static void
+e_comp_editor_page_schedule_dispose (GObject *object)
+{
+       ECompEditorPageSchedule *page_schedule;
+       ECompEditor *comp_editor;
+
+       page_schedule = E_COMP_EDITOR_PAGE_SCHEDULE (object);
+
+       comp_editor = e_comp_editor_page_ref_editor (E_COMP_EDITOR_PAGE (page_schedule));
+       if (comp_editor) {
+               g_signal_handlers_disconnect_by_func (comp_editor,
+                       G_CALLBACK (ecep_schedule_editor_times_changed_cb), page_schedule);
+               g_clear_object (&comp_editor);
+       }
+
+       g_clear_object (&page_schedule->priv->store);
+
+       G_OBJECT_CLASS (e_comp_editor_page_schedule_parent_class)->dispose (object);
+}
+
+static void
+e_comp_editor_page_schedule_init (ECompEditorPageSchedule *page_schedule)
+{
+       page_schedule->priv = G_TYPE_INSTANCE_GET_PRIVATE (page_schedule,
+               E_TYPE_COMP_EDITOR_PAGE_SCHEDULE,
+               ECompEditorPageSchedulePrivate);
+}
+
+static void
+e_comp_editor_page_schedule_class_init (ECompEditorPageScheduleClass *klass)
+{
+       ECompEditorPageClass *page_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPageSchedulePrivate));
+
+       page_class = E_COMP_EDITOR_PAGE_CLASS (klass);
+       page_class->sensitize_widgets = ecep_schedule_sensitize_widgets;
+       page_class->fill_widgets = ecep_schedule_fill_widgets;
+       page_class->fill_component = ecep_schedule_fill_component;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = e_comp_editor_page_schedule_set_property;
+       object_class->get_property = e_comp_editor_page_schedule_get_property;
+       object_class->constructed = e_comp_editor_page_schedule_constructed;
+       object_class->dispose = e_comp_editor_page_schedule_dispose;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_STORE,
+               g_param_spec_object (
+                       "store",
+                       "store",
+                       "an EMeetingStore",
+                       E_TYPE_MEETING_STORE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+ECompEditorPage *
+e_comp_editor_page_schedule_new (ECompEditor *editor,
+                                EMeetingStore *meeting_store)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (editor), NULL);
+
+       return g_object_new (E_TYPE_COMP_EDITOR_PAGE_SCHEDULE,
+               "editor", editor,
+               "store", meeting_store,
+               NULL);
+}
+
+EMeetingStore *
+e_comp_editor_page_schedule_get_store (ECompEditorPageSchedule *page_schedule)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule), NULL);
+
+       return page_schedule->priv->store;
+}
+
+EMeetingTimeSelector *
+e_comp_editor_page_schedule_get_time_selector (ECompEditorPageSchedule *page_schedule)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE_SCHEDULE (page_schedule), NULL);
+
+       return page_schedule->priv->selector;
+}
diff --git a/calendar/gui/e-comp-editor-page-schedule.h b/calendar/gui/e-comp-editor-page-schedule.h
new file mode 100644
index 0000000..a8a0536
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page-schedule.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PAGE_SCHEDULE_H
+#define E_COMP_EDITOR_PAGE_SCHEDULE_H
+
+#include <calendar/gui/e-comp-editor.h>
+#include <calendar/gui/e-comp-editor-page.h>
+#include <calendar/gui/e-meeting-store.h>
+#include <calendar/gui/e-meeting-time-sel.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PAGE_SCHEDULE \
+       (e_comp_editor_page_schedule_get_type ())
+#define E_COMP_EDITOR_PAGE_SCHEDULE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_SCHEDULE, ECompEditorPageSchedule))
+#define E_COMP_EDITOR_PAGE_SCHEDULE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_SCHEDULE, ECompEditorPageScheduleClass))
+#define E_IS_COMP_EDITOR_PAGE_SCHEDULE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_SCHEDULE))
+#define E_IS_COMP_EDITOR_PAGE_SCHEDULE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE_SCHEDULE))
+#define E_COMP_EDITOR_PAGE_SCHEDULE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE_SCHEDULE, ECompEditorPageScheduleClass))
+
+typedef struct _ECompEditorPageSchedule ECompEditorPageSchedule;
+typedef struct _ECompEditorPageScheduleClass ECompEditorPageScheduleClass;
+typedef struct _ECompEditorPageSchedulePrivate ECompEditorPageSchedulePrivate;
+
+struct _ECompEditorPageSchedule {
+       ECompEditorPage parent;
+
+       ECompEditorPageSchedulePrivate *priv;
+};
+
+struct _ECompEditorPageScheduleClass {
+       ECompEditorPageClass parent_class;
+};
+
+GType          e_comp_editor_page_schedule_get_type    (void) G_GNUC_CONST;
+ECompEditorPage *
+               e_comp_editor_page_schedule_new         (ECompEditor *editor,
+                                                        EMeetingStore *meeting_store);
+EMeetingStore *        e_comp_editor_page_schedule_get_store   (ECompEditorPageSchedule *page_schedule);
+EMeetingTimeSelector *
+               e_comp_editor_page_schedule_get_time_selector
+                                                       (ECompEditorPageSchedule *page_schedule);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PAGE_SCHEDULE_H */
diff --git a/calendar/gui/e-comp-editor-page.c b/calendar/gui/e-comp-editor-page.c
new file mode 100644
index 0000000..896eed9
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "e-comp-editor.h"
+#include "e-comp-editor-page.h"
+
+struct _ECompEditorPagePrivate {
+       GWeakRef editor; /* Holds an ECompEditor * */
+
+       GSList *parts; /* PropertyPartData * */
+};
+
+enum {
+       PROP_0,
+       PROP_EDITOR
+};
+
+enum {
+       CHANGED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPage, e_comp_editor_page, GTK_TYPE_GRID)
+
+typedef struct _ProperyPartData {
+       ECompEditorPropertyPart *part;
+       gulong changed_handler_id;
+} PropertyPartData;
+
+static void
+property_part_data_free (gpointer ptr)
+{
+       PropertyPartData *ppd = ptr;
+
+       if (ppd) {
+               if (ppd->changed_handler_id)
+                       g_signal_handler_disconnect (ppd->part, ppd->changed_handler_id);
+               g_clear_object (&ppd->part);
+               g_free (ppd);
+       }
+}
+
+static void
+ecep_sensitize_widgets (ECompEditorPage *page,
+                       gboolean force_insensitive)
+{
+       GSList *link;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       for (link = page->priv->parts; link; link = g_slist_next (link)) {
+               PropertyPartData *ppd = link->data;
+               GtkWidget *widget;
+
+               g_warn_if_fail (ppd != NULL);
+               if (!ppd)
+                       continue;
+
+               widget = e_comp_editor_property_part_get_label_widget (ppd->part);
+               if (widget)
+                       gtk_widget_set_sensitive (widget, !force_insensitive);
+
+               widget = e_comp_editor_property_part_get_edit_widget (ppd->part);
+               if (widget)
+                       gtk_widget_set_sensitive (widget, !force_insensitive);
+       }
+}
+
+static void
+ecep_fill_widgets (ECompEditorPage *page,
+                  icalcomponent *component)
+{
+       GSList *link;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+       g_return_if_fail (component != NULL);
+
+       for (link = page->priv->parts; link; link = g_slist_next (link)) {
+               PropertyPartData *ppd = link->data;
+
+               g_warn_if_fail (ppd != NULL);
+               if (!ppd)
+                       continue;
+
+               e_comp_editor_property_part_fill_widget (ppd->part, component);
+       }
+}
+
+static gboolean
+ecep_fill_component (ECompEditorPage *page,
+                    icalcomponent *component)
+{
+       GSList *link;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       for (link = page->priv->parts; link; link = g_slist_next (link)) {
+               PropertyPartData *ppd = link->data;
+
+               g_warn_if_fail (ppd != NULL);
+               if (!ppd)
+                       continue;
+
+               e_comp_editor_property_part_fill_component (ppd->part, component);
+       }
+
+       return TRUE;
+}
+
+static void
+e_comp_editor_page_set_editor (ECompEditorPage *page,
+                              ECompEditor *editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+       g_return_if_fail (E_IS_COMP_EDITOR (editor));
+
+       g_weak_ref_set (&page->priv->editor, editor);
+}
+
+static void
+e_comp_editor_page_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_EDITOR:
+                       e_comp_editor_page_set_editor (
+                               E_COMP_EDITOR_PAGE (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_page_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_EDITOR:
+                       g_value_take_object (
+                               value,
+                               e_comp_editor_page_ref_editor (
+                               E_COMP_EDITOR_PAGE (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_page_constructed (GObject *object)
+{
+       G_OBJECT_CLASS (e_comp_editor_page_parent_class)->constructed (object);
+
+       gtk_widget_show (GTK_WIDGET (object));
+
+       g_object_set (object,
+               "column-spacing", 4,
+               "row-spacing", 4,
+               "border-width", 6,
+               NULL);
+}
+
+static void
+e_comp_editor_page_finalize (GObject *object)
+{
+       ECompEditorPage *page = E_COMP_EDITOR_PAGE (object);
+
+       g_weak_ref_clear (&page->priv->editor);
+
+       g_slist_free_full (page->priv->parts, property_part_data_free);
+       page->priv->parts = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_page_parent_class)->finalize (object);
+}
+
+static void
+e_comp_editor_page_init (ECompEditorPage *page)
+{
+       page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page, E_TYPE_COMP_EDITOR_PAGE, ECompEditorPagePrivate);
+
+       g_weak_ref_init (&page->priv->editor, NULL);
+}
+
+static void
+e_comp_editor_page_class_init (ECompEditorPageClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPagePrivate));
+
+       klass->sensitize_widgets = ecep_sensitize_widgets;
+       klass->fill_widgets = ecep_fill_widgets;
+       klass->fill_component = ecep_fill_component;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = e_comp_editor_page_set_property;
+       object_class->get_property = e_comp_editor_page_get_property;
+       object_class->constructed = e_comp_editor_page_constructed;
+       object_class->finalize = e_comp_editor_page_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_EDITOR,
+               g_param_spec_object (
+                       "editor",
+                       "Editor",
+                       "ECompEditor the page belongs to",
+                       E_TYPE_COMP_EDITOR,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       signals[CHANGED] = g_signal_new (
+               "changed",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (ECompEditorPageClass, changed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+}
+
+ECompEditor *
+e_comp_editor_page_ref_editor (ECompEditorPage *page)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE (page), NULL);
+
+       return g_weak_ref_get (&page->priv->editor);
+}
+
+/* This consumes the 'part'. */
+void
+e_comp_editor_page_add_property_part (ECompEditorPage *page,
+                                     ECompEditorPropertyPart *part,
+                                     gint attach_left,
+                                     gint attach_top,
+                                     gint attach_width,
+                                     gint attach_height)
+{
+       GtkWidget *label_widget;
+       GtkWidget *edit_widget;
+       PropertyPartData *ppd;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (part));
+
+       label_widget = e_comp_editor_property_part_get_label_widget (part);
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+
+       g_return_if_fail (label_widget != NULL || edit_widget != NULL);
+
+       ppd = g_new0 (PropertyPartData, 1);
+       ppd->part = part; /* takes ownership */
+       ppd->changed_handler_id = g_signal_connect_swapped (part, "changed",
+               G_CALLBACK (e_comp_editor_page_emit_changed), page);
+
+       if (label_widget) {
+               gtk_grid_attach (GTK_GRID (page), label_widget, attach_left, attach_top, 1, attach_height);
+       }
+       if (edit_widget) {
+               gint inc = label_widget ? 1 : 0;
+
+               gtk_grid_attach (GTK_GRID (page), edit_widget, attach_left + inc, attach_top, MAX 
(attach_width - inc, 1), attach_height);
+       }
+
+       page->priv->parts = g_slist_append (page->priv->parts, ppd);
+}
+
+void
+e_comp_editor_page_sensitize_widgets (ECompEditorPage *page,
+                                     gboolean force_insensitive)
+{
+       ECompEditorPageClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       klass = E_COMP_EDITOR_PAGE_GET_CLASS (page);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->sensitize_widgets != NULL);
+
+       klass->sensitize_widgets (page, force_insensitive);
+}
+
+void
+e_comp_editor_page_fill_widgets (ECompEditorPage *page,
+                                icalcomponent *component)
+{
+       ECompEditorPageClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+       g_return_if_fail (component != NULL);
+
+       klass = E_COMP_EDITOR_PAGE_GET_CLASS (page);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->fill_widgets != NULL);
+
+       e_comp_editor_page_set_updating (page, TRUE);
+
+       klass->fill_widgets (page, component);
+
+       e_comp_editor_page_set_updating (page, FALSE);
+}
+
+gboolean
+e_comp_editor_page_fill_component (ECompEditorPage *page,
+                                  icalcomponent *component)
+{
+       ECompEditorPageClass *klass;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE (page), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       klass = E_COMP_EDITOR_PAGE_GET_CLASS (page);
+       g_return_val_if_fail (klass != NULL, FALSE);
+       g_return_val_if_fail (klass->fill_component != NULL, FALSE);
+
+       return klass->fill_component (page, component);
+}
+
+void
+e_comp_editor_page_emit_changed (ECompEditorPage *page)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       g_signal_emit (page, signals[CHANGED], 0, NULL);
+}
+
+gboolean
+e_comp_editor_page_get_updating (ECompEditorPage *page)
+{
+       ECompEditor *comp_editor;
+       gboolean updating = FALSE;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PAGE (page), FALSE);
+
+       comp_editor = e_comp_editor_page_ref_editor (page);
+       if (comp_editor)
+               updating = e_comp_editor_get_updating (comp_editor);
+       g_clear_object (&comp_editor);
+
+       return updating;
+}
+
+void
+e_comp_editor_page_set_updating (ECompEditorPage *page,
+                                gboolean updating)
+{
+       ECompEditor *comp_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       comp_editor = e_comp_editor_page_ref_editor (page);
+       if (comp_editor)
+               e_comp_editor_set_updating (comp_editor, updating);
+       g_clear_object (&comp_editor);
+}
+
+void
+e_comp_editor_page_select (ECompEditorPage *page)
+{
+       ECompEditor *comp_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       comp_editor = e_comp_editor_page_ref_editor (page);
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       e_comp_editor_select_page (comp_editor, page);
+
+       g_clear_object (&comp_editor);
+}
diff --git a/calendar/gui/e-comp-editor-page.h b/calendar/gui/e-comp-editor-page.h
new file mode 100644
index 0000000..114cbdf
--- /dev/null
+++ b/calendar/gui/e-comp-editor-page.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PAGE_H
+#define E_COMP_EDITOR_PAGE_H
+
+#include <gtk/gtk.h>
+#include <libecal/libecal.h>
+
+#include <calendar/gui/e-comp-editor-property-part.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PAGE \
+       (e_comp_editor_page_get_type ())
+#define E_COMP_EDITOR_PAGE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE, ECompEditorPage))
+#define E_COMP_EDITOR_PAGE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE, ECompEditorPageClass))
+#define E_IS_COMP_EDITOR_PAGE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE))
+#define E_IS_COMP_EDITOR_PAGE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PAGE))
+#define E_COMP_EDITOR_PAGE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PAGE, ECompEditorPageClass))
+
+G_BEGIN_DECLS
+
+struct _ECompEditor;
+
+typedef struct _ECompEditorPage ECompEditorPage;
+typedef struct _ECompEditorPageClass ECompEditorPageClass;
+typedef struct _ECompEditorPagePrivate ECompEditorPagePrivate;
+
+struct _ECompEditorPage {
+       GtkGrid parent;
+
+       ECompEditorPagePrivate *priv;
+};
+
+struct _ECompEditorPageClass {
+       GtkGridClass parent_class;
+
+       /* Virtual functions */
+       void            (* sensitize_widgets)   (ECompEditorPage *page,
+                                                gboolean force_insensitive);
+       void            (* fill_widgets)        (ECompEditorPage *page,
+                                                icalcomponent *component);
+       gboolean        (* fill_component)      (ECompEditorPage *page,
+                                                icalcomponent *component);
+
+       /* Signals */
+       void            (* changed)             (ECompEditorPage *page);
+};
+
+GType          e_comp_editor_page_get_type             (void) G_GNUC_CONST;
+
+struct _ECompEditor *
+               e_comp_editor_page_ref_editor           (ECompEditorPage *page);
+void           e_comp_editor_page_add_property_part    (ECompEditorPage *page,
+                                                        ECompEditorPropertyPart *part,
+                                                        gint attach_left,
+                                                        gint attach_top,
+                                                        gint attach_width,
+                                                        gint attach_height);
+void           e_comp_editor_page_sensitize_widgets    (ECompEditorPage *page,
+                                                        gboolean force_insensitive);
+void           e_comp_editor_page_fill_widgets         (ECompEditorPage *page,
+                                                        icalcomponent *component);
+gboolean       e_comp_editor_page_fill_component       (ECompEditorPage *page,
+                                                        icalcomponent *component);
+void           e_comp_editor_page_emit_changed         (ECompEditorPage *page);
+gboolean       e_comp_editor_page_get_updating         (ECompEditorPage *page);
+void           e_comp_editor_page_set_updating         (ECompEditorPage *page,
+                                                        gboolean updating);
+void           e_comp_editor_page_select               (ECompEditorPage *page);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PAGE_H */
diff --git a/calendar/gui/e-comp-editor-property-part.c b/calendar/gui/e-comp-editor-property-part.c
new file mode 100644
index 0000000..306376d
--- /dev/null
+++ b/calendar/gui/e-comp-editor-property-part.c
@@ -0,0 +1,1559 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "comp-util.h"
+#include "e-comp-editor-property-part.h"
+
+struct _ECompEditorPropertyPartPrivate {
+       GtkWidget *label_widget;
+       GtkWidget *edit_widget;
+       gboolean visible;
+};
+
+enum {
+       PROPERTY_PART_PROP_0,
+       PROPERTY_PART_PROP_VISIBLE
+};
+
+enum {
+       PROPERTY_PART_CHANGED,
+       PROPERTY_PART_LAST_SIGNAL
+};
+
+static guint property_part_signals[PROPERTY_PART_LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPart, e_comp_editor_property_part, G_TYPE_OBJECT)
+
+static void
+e_comp_editor_property_part_set_property (GObject *object,
+                                         guint property_id,
+                                         const GValue *value,
+                                         GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROPERTY_PART_PROP_VISIBLE:
+                       e_comp_editor_property_part_set_visible (
+                               E_COMP_EDITOR_PROPERTY_PART (object),
+                               g_value_get_boolean (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_property_part_get_property (GObject *object,
+                                         guint property_id,
+                                         GValue *value,
+                                         GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROPERTY_PART_PROP_VISIBLE:
+                       g_value_set_boolean (
+                               value,
+                               e_comp_editor_property_part_get_visible (
+                               E_COMP_EDITOR_PROPERTY_PART (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_property_part_constructed (GObject *object)
+{
+       ECompEditorPropertyPart *property_part;
+       GtkWidget *label_widget = NULL, *edit_widget = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_property_part_parent_class)->constructed (object);
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (object));
+
+       property_part = E_COMP_EDITOR_PROPERTY_PART (object);
+
+       e_comp_editor_property_part_create_widgets (property_part, &label_widget, &edit_widget);
+
+       if (label_widget) {
+               property_part->priv->label_widget = g_object_ref_sink (label_widget);
+
+               e_binding_bind_property (
+                       property_part, "visible",
+                       label_widget, "visible",
+                       G_BINDING_SYNC_CREATE);
+       }
+
+       if (edit_widget) {
+               property_part->priv->edit_widget = g_object_ref_sink (edit_widget);
+
+               e_binding_bind_property (
+                       property_part, "visible",
+                       edit_widget, "visible",
+                       G_BINDING_SYNC_CREATE);
+       }
+}
+
+static void
+e_comp_editor_property_part_dispose (GObject *object)
+{
+       ECompEditorPropertyPart *property_part;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (object));
+
+       property_part = E_COMP_EDITOR_PROPERTY_PART (object);
+
+       g_clear_object (&property_part->priv->label_widget);
+       g_clear_object (&property_part->priv->edit_widget);
+
+       G_OBJECT_CLASS (e_comp_editor_property_part_parent_class)->dispose (object);
+}
+
+static void
+e_comp_editor_property_part_init (ECompEditorPropertyPart *property_part)
+{
+       property_part->priv = G_TYPE_INSTANCE_GET_PRIVATE (property_part,
+               E_TYPE_COMP_EDITOR_PROPERTY_PART,
+               ECompEditorPropertyPartPrivate);
+       property_part->priv->visible = TRUE;
+}
+
+static void
+e_comp_editor_property_part_class_init (ECompEditorPropertyPartClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = e_comp_editor_property_part_set_property;
+       object_class->get_property = e_comp_editor_property_part_get_property;
+       object_class->constructed = e_comp_editor_property_part_constructed;
+       object_class->dispose = e_comp_editor_property_part_dispose;
+
+       g_object_class_install_property (
+               object_class,
+               PROPERTY_PART_PROP_VISIBLE,
+               g_param_spec_boolean (
+                       "visible",
+                       "Visible",
+                       "Whether the part is visible",
+                       TRUE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       property_part_signals[PROPERTY_PART_CHANGED] = g_signal_new (
+               "changed",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (ECompEditorPropertyPartClass, changed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+}
+
+gboolean
+e_comp_editor_property_part_get_visible (ECompEditorPropertyPart *property_part)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), FALSE);
+
+       return property_part->priv->visible;
+}
+
+void
+e_comp_editor_property_part_set_visible (ECompEditorPropertyPart *property_part,
+                                        gboolean visible)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
+
+       if ((property_part->priv->visible ? 1 : 0) == (visible ? 1 : 0))
+               return;
+
+       property_part->priv->visible = visible;
+
+       g_object_notify (G_OBJECT (property_part), "visible");
+}
+
+void
+e_comp_editor_property_part_create_widgets (ECompEditorPropertyPart *property_part,
+                                           GtkWidget **out_label_widget,
+                                           GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
+
+       g_return_if_fail (property_part->priv->label_widget == NULL);
+       g_return_if_fail (property_part->priv->edit_widget == NULL);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
+       g_return_if_fail (klass->create_widgets != NULL);
+
+       klass->create_widgets (property_part, out_label_widget, out_edit_widget);
+}
+
+GtkWidget *
+e_comp_editor_property_part_get_label_widget (ECompEditorPropertyPart *property_part)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), NULL);
+
+       return property_part->priv->label_widget;
+}
+
+GtkWidget *
+e_comp_editor_property_part_get_edit_widget (ECompEditorPropertyPart *property_part)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), NULL);
+
+       return property_part->priv->edit_widget;
+}
+
+void
+e_comp_editor_property_part_fill_widget (ECompEditorPropertyPart *property_part,
+                                        icalcomponent *component)
+{
+       ECompEditorPropertyPartClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
+       g_return_if_fail (klass->fill_widget != NULL);
+
+       klass->fill_widget (property_part, component);
+}
+
+void
+e_comp_editor_property_part_fill_component (ECompEditorPropertyPart *property_part,
+                                           icalcomponent *component)
+{
+       ECompEditorPropertyPartClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
+       g_return_if_fail (klass->fill_component != NULL);
+
+       klass->fill_component (property_part, component);
+}
+
+void
+e_comp_editor_property_part_emit_changed (ECompEditorPropertyPart *property_part)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
+
+       g_signal_emit (property_part, property_part_signals[PROPERTY_PART_CHANGED], 0, NULL);
+}
+
+/* ************************************************************************* */
+
+struct _ECompEditorPropertyPartStringPrivate {
+       gint dummy;
+};
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartString, e_comp_editor_property_part_string, 
E_TYPE_COMP_EDITOR_PROPERTY_PART)
+
+static void
+ecepp_string_create_widgets (ECompEditorPropertyPart *property_part,
+                            GtkWidget **out_label_widget,
+                            GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartStringClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (g_type_is_a (klass->entry_type, GTK_TYPE_ENTRY) ||
+                         g_type_is_a (klass->entry_type, GTK_TYPE_TEXT_VIEW));
+
+       /* The descendant sets the 'out_label_widget' parameter */
+       *out_edit_widget = g_object_new (klass->entry_type, NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       g_object_set (G_OBJECT (*out_edit_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       gtk_widget_show (*out_edit_widget);
+
+       if (g_type_is_a (klass->entry_type, GTK_TYPE_TEXT_VIEW)) {
+               GtkScrolledWindow *scrolled_window;
+               GtkTextBuffer *text_buffer;
+
+               scrolled_window = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
+               gtk_scrolled_window_set_policy (scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+               gtk_scrolled_window_set_shadow_type (scrolled_window, GTK_SHADOW_IN);
+               gtk_widget_show (GTK_WIDGET (scrolled_window));
+
+               gtk_container_add (GTK_CONTAINER (scrolled_window), *out_edit_widget);
+
+               g_object_set (G_OBJECT (*out_edit_widget),
+                       "hexpand", TRUE,
+                       "halign", GTK_ALIGN_FILL,
+                       "vexpand", TRUE,
+                       "valign", GTK_ALIGN_FILL,
+                       NULL);
+
+               g_object_set (G_OBJECT (scrolled_window),
+                       "hexpand", FALSE,
+                       "halign", GTK_ALIGN_FILL,
+                       "vexpand", FALSE,
+                       "valign", GTK_ALIGN_START,
+                       NULL);
+
+               text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (*out_edit_widget));
+               g_signal_connect_swapped (text_buffer, "changed",
+                       G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+
+               *out_edit_widget = GTK_WIDGET (scrolled_window);
+       } else {
+               g_signal_connect_swapped (*out_edit_widget, "changed",
+                       G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+       }
+}
+
+static void
+ecepp_string_fill_widget (ECompEditorPropertyPart *property_part,
+                         icalcomponent *component)
+{
+       ECompEditorPropertyPartStringClass *klass;
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+       const gchar *value = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_SCROLLED_WINDOW (edit_widget));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (klass->ical_get_func != NULL);
+
+       prop = icalcomponent_get_first_property (component, klass->ical_prop_kind);
+       if (prop)
+               value = klass->ical_get_func (prop);
+
+       if (!value)
+               value = "";
+
+       if (GTK_IS_ENTRY (edit_widget)) {
+               gtk_entry_set_text (GTK_ENTRY (edit_widget), value);
+       } else /* if (GTK_IS_SCROLLED_WINDOW (edit_widget)) */ {
+               GtkTextBuffer *buffer;
+
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (edit_widget))));
+               gtk_text_buffer_set_text (buffer, value, -1);
+       }
+
+       e_widget_undo_reset (edit_widget);
+}
+
+static void
+ecepp_string_fill_component (ECompEditorPropertyPart *property_part,
+                            icalcomponent *component)
+{
+       ECompEditorPropertyPartStringClass *klass;
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+       gchar *value;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_SCROLLED_WINDOW (edit_widget));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (klass->ical_new_func != NULL);
+       g_return_if_fail (klass->ical_set_func != NULL);
+
+       if (GTK_IS_ENTRY (edit_widget)) {
+               value = g_strdup (gtk_entry_get_text (GTK_ENTRY (edit_widget)));
+       } else /* if (GTK_IS_SCROLLED_WINDOW (edit_widget)) */ {
+               GtkTextBuffer *buffer;
+               GtkTextIter text_iter_start, text_iter_end;
+
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (edit_widget))));
+               gtk_text_buffer_get_start_iter (buffer, &text_iter_start);
+               gtk_text_buffer_get_end_iter (buffer, &text_iter_end);
+               value = gtk_text_buffer_get_text (buffer, &text_iter_start, &text_iter_end, FALSE);
+       }
+
+       prop = icalcomponent_get_first_property (component, klass->ical_prop_kind);
+
+       if (value && *value) {
+               if (prop) {
+                       klass->ical_set_func (prop, value);
+               } else {
+                       prop = klass->ical_new_func (value);
+                       icalcomponent_add_property (component, prop);
+               }
+       } else if (prop) {
+               icalcomponent_remove_property (component, prop);
+               icalproperty_free (prop);
+       }
+
+       g_free (value);
+}
+
+static void
+e_comp_editor_property_part_string_init (ECompEditorPropertyPartString *part_string)
+{
+       part_string->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_string,
+               E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING,
+               ECompEditorPropertyPartStringPrivate);
+}
+
+static void
+e_comp_editor_property_part_string_class_init (ECompEditorPropertyPartStringClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartStringPrivate));
+
+       klass->entry_type = GTK_TYPE_ENTRY;
+
+       klass->ical_prop_kind = ICAL_NO_PROPERTY;
+       klass->ical_new_func = NULL;
+       klass->ical_set_func = NULL;
+       klass->ical_get_func = NULL;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_string_create_widgets;
+       part_class->fill_widget = ecepp_string_fill_widget;
+       part_class->fill_component = ecepp_string_fill_component;
+}
+
+void
+e_comp_editor_property_part_string_attach_focus_tracker (ECompEditorPropertyPartString *part_string,
+                                                        EFocusTracker *focus_tracker)
+{
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string));
+
+       if (!focus_tracker)
+               return;
+
+       g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_string));
+       if (edit_widget)
+               e_widget_undo_attach (edit_widget, focus_tracker);
+}
+
+/* ************************************************************************* */
+
+struct _ECompEditorPropertyPartDatetimePrivate {
+       GWeakRef timezone_entry;
+};
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartDatetime, e_comp_editor_property_part_datetime, 
E_TYPE_COMP_EDITOR_PROPERTY_PART)
+
+static void
+ecepp_datetime_create_widgets (ECompEditorPropertyPart *property_part,
+                              GtkWidget **out_label_widget,
+                              GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartDatetimeClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+
+       /* The descendant sets the 'out_label_widget' parameter */
+       *out_edit_widget = e_date_edit_new ();
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       g_object_set (G_OBJECT (*out_edit_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       gtk_widget_show (*out_edit_widget);
+
+       g_signal_connect_swapped (*out_edit_widget, "changed",
+               G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+       g_signal_connect_swapped (*out_edit_widget, "notify::show-time",
+               G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+}
+
+static void
+ecepp_datetime_fill_widget (ECompEditorPropertyPart *property_part,
+                           icalcomponent *component)
+{
+       ECompEditorPropertyPartDatetime *part_datetime;
+       ECompEditorPropertyPartDatetimeClass *klass;
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+       struct icaltimetype value;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (klass->ical_get_func != NULL);
+
+       part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part);
+
+       prop = icalcomponent_get_first_property (component, klass->ical_prop_kind);
+       if (prop)
+               value = klass->ical_get_func (prop);
+       else
+               value = icaltime_null_time ();
+
+       e_comp_editor_property_part_datetime_set_value (part_datetime, value);
+}
+
+static void
+ecepp_datetime_fill_component (ECompEditorPropertyPart *property_part,
+                              icalcomponent *component)
+{
+       ECompEditorPropertyPartDatetime *part_datetime;
+       ECompEditorPropertyPartDatetimeClass *klass;
+       GtkWidget *edit_widget;
+       EDateEdit *date_edit;
+       icalproperty *prop;
+       struct icaltimetype value;
+       time_t tt;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (klass->ical_new_func != NULL);
+       g_return_if_fail (klass->ical_set_func != NULL);
+
+       part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part);
+       date_edit = E_DATE_EDIT (edit_widget);
+       tt = e_date_edit_get_time (date_edit);
+
+       prop = icalcomponent_get_first_property (component, klass->ical_prop_kind);
+
+       if (e_date_edit_get_allow_no_date_set (date_edit) && tt == (time_t) -1) {
+               if (prop) {
+                       icalcomponent_remove_property (component, prop);
+                       icalproperty_free (prop);
+               }
+       } else {
+               value = e_comp_editor_property_part_datetime_get_value (part_datetime);
+
+               if (prop) {
+                       klass->ical_set_func (prop, value);
+                       cal_comp_util_update_tzid_parameter (prop, value);
+               } else {
+                       prop = klass->ical_new_func (value);
+                       cal_comp_util_update_tzid_parameter (prop, value);
+                       icalcomponent_add_property (component, prop);
+               }
+       }
+}
+
+static void
+ecepp_datetime_finalize (GObject *object)
+{
+       ECompEditorPropertyPartDatetime *part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (object);
+
+       g_weak_ref_clear (&part_datetime->priv->timezone_entry);
+
+       G_OBJECT_CLASS (e_comp_editor_property_part_datetime_parent_class)->finalize (object);
+}
+
+static void
+e_comp_editor_property_part_datetime_init (ECompEditorPropertyPartDatetime *part_datetime)
+{
+       part_datetime->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_datetime,
+               E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME,
+               ECompEditorPropertyPartDatetimePrivate);
+
+       g_weak_ref_init (&part_datetime->priv->timezone_entry, NULL);
+}
+
+static void
+e_comp_editor_property_part_datetime_class_init (ECompEditorPropertyPartDatetimeClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartDatetimePrivate));
+
+       klass->ical_prop_kind = ICAL_NO_PROPERTY;
+       klass->ical_new_func = NULL;
+       klass->ical_set_func = NULL;
+       klass->ical_get_func = NULL;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_datetime_create_widgets;
+       part_class->fill_widget = ecepp_datetime_fill_widget;
+       part_class->fill_component = ecepp_datetime_fill_component;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = ecepp_datetime_finalize;
+}
+
+void
+e_comp_editor_property_part_datetime_attach_timezone_entry (ECompEditorPropertyPartDatetime *part_datetime,
+                                                           ETimezoneEntry *timezone_entry)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
+       if (timezone_entry)
+               g_return_if_fail (E_IS_TIMEZONE_ENTRY (timezone_entry));
+
+       g_weak_ref_set (&part_datetime->priv->timezone_entry, timezone_entry);
+}
+
+void
+e_comp_editor_property_part_datetime_set_date_only (ECompEditorPropertyPartDatetime *part_datetime,
+                                                   gboolean date_only)
+{
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
+
+       if ((e_date_edit_get_show_time (E_DATE_EDIT (edit_widget)) ? 1 : 0) == ((!date_only) ? 1 : 0))
+               return;
+
+       e_date_edit_set_show_time (E_DATE_EDIT (edit_widget), !date_only);
+}
+
+gboolean
+e_comp_editor_property_part_datetime_get_date_only (ECompEditorPropertyPartDatetime *part_datetime)
+{
+       GtkWidget *edit_widget;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), FALSE);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), FALSE);
+
+       return !e_date_edit_get_show_time (E_DATE_EDIT (edit_widget));
+}
+
+void
+e_comp_editor_property_part_datetime_set_allow_no_date_set (ECompEditorPropertyPartDatetime *part_datetime,
+                                                           gboolean allow_no_date_set)
+{
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
+
+       e_date_edit_set_allow_no_date_set (E_DATE_EDIT (edit_widget), allow_no_date_set);
+}
+
+gboolean
+e_comp_editor_property_part_datetime_get_allow_no_date_set (ECompEditorPropertyPartDatetime *part_datetime)
+{
+       GtkWidget *edit_widget;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), FALSE);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), FALSE);
+
+       return !e_date_edit_get_allow_no_date_set (E_DATE_EDIT (edit_widget));
+}
+
+void
+e_comp_editor_property_part_datetime_set_value (ECompEditorPropertyPartDatetime *part_datetime,
+                                               struct icaltimetype value)
+{
+       GtkWidget *edit_widget;
+       EDateEdit *date_edit;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
+
+       date_edit = E_DATE_EDIT (edit_widget);
+
+       if (!e_date_edit_get_allow_no_date_set (date_edit) && (icaltime_is_null_time (value) ||
+           !icaltime_is_valid_time (value))) {
+               value = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+       }
+
+       if (icaltime_is_null_time (value) ||
+           !icaltime_is_valid_time (value)) {
+               e_date_edit_set_time (date_edit, (time_t) -1);
+       } else {
+               e_date_edit_set_date (date_edit, value.year, value.month, value.day);
+
+               if (!value.is_date)
+                       e_date_edit_set_time_of_day (date_edit, value.hour, value.minute);
+               else if (e_date_edit_get_show_time (date_edit))
+                       e_date_edit_set_time_of_day (date_edit, 0, 0);
+
+               e_comp_editor_property_part_datetime_set_date_only (part_datetime, value.is_date);
+       }
+}
+
+struct icaltimetype
+e_comp_editor_property_part_datetime_get_value (ECompEditorPropertyPartDatetime *part_datetime)
+{
+       ETimezoneEntry *timezone_entry = NULL;
+       GtkWidget *edit_widget;
+       EDateEdit *date_edit;
+       struct icaltimetype value = icaltime_null_time ();
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), value);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), value);
+
+       date_edit = E_DATE_EDIT (edit_widget);
+
+       if (!e_date_edit_get_date (date_edit, &value.year, &value.month, &value.day))
+               return icaltime_null_time ();
+
+       if (!e_date_edit_get_show_time (date_edit)) {
+               value.is_date = 1;
+       } else {
+               value.is_date = 0;
+               value.zone = NULL;
+
+               e_date_edit_get_time_of_day (date_edit, &value.hour, &value.minute);
+
+               timezone_entry = g_weak_ref_get (&part_datetime->priv->timezone_entry);
+               if (timezone_entry)
+                       value.zone = e_timezone_entry_get_timezone (timezone_entry);
+               if (!value.zone)
+                       value.zone = icaltimezone_get_utc_timezone ();
+
+               value.is_utc = value.zone == icaltimezone_get_utc_timezone ();
+       }
+
+       g_clear_object (&timezone_entry);
+
+       return value;
+}
+
+gboolean
+e_comp_editor_property_part_datetime_check_validity (ECompEditorPropertyPartDatetime *part_datetime,
+                                                    gboolean *out_date_is_valid,
+                                                    gboolean *out_time_is_valid)
+{
+       GtkWidget *edit_widget;
+       EDateEdit *date_edit;
+       gboolean date_is_valid = TRUE, time_is_valid = TRUE;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), FALSE);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART 
(part_datetime));
+       g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), FALSE);
+
+       date_edit = E_DATE_EDIT (edit_widget);
+
+       if (!e_date_edit_get_allow_no_date_set (date_edit) ||
+           e_date_edit_get_time (date_edit) != (time_t) -1) {
+               date_is_valid = e_date_edit_date_is_valid (date_edit);
+
+               if (e_date_edit_get_show_time (date_edit))
+                       time_is_valid = e_date_edit_time_is_valid (date_edit);
+       }
+
+       if (out_date_is_valid)
+               *out_date_is_valid = date_is_valid;
+       if (out_time_is_valid)
+               *out_time_is_valid = time_is_valid;
+
+       return date_is_valid && time_is_valid;
+}
+
+/* ************************************************************************* */
+
+struct _ECompEditorPropertyPartSpinPrivate {
+       gint dummy;
+};
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartSpin, e_comp_editor_property_part_spin, 
E_TYPE_COMP_EDITOR_PROPERTY_PART)
+
+static void
+ecepp_spin_create_widgets (ECompEditorPropertyPart *property_part,
+                          GtkWidget **out_label_widget,
+                          GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartSpinClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+
+       /* The descendant sets the 'out_label_widget' parameter */
+       *out_edit_widget = gtk_spin_button_new_with_range (-10, 10, 1);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       g_object_set (G_OBJECT (*out_edit_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (*out_edit_widget), 0);
+
+       gtk_widget_show (*out_edit_widget);
+
+       g_signal_connect_swapped (*out_edit_widget, "value-changed",
+               G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+}
+
+static void
+ecepp_spin_fill_widget (ECompEditorPropertyPart *property_part,
+                       icalcomponent *component)
+{
+       ECompEditorPropertyPartSpinClass *klass;
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+       gint value;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (klass->ical_get_func != NULL);
+
+       prop = icalcomponent_get_first_property (component, klass->ical_prop_kind);
+       if (prop) {
+               value = klass->ical_get_func (prop);
+       } else {
+               gdouble d_min, d_max;
+
+               gtk_spin_button_get_range (GTK_SPIN_BUTTON (edit_widget), &d_min, &d_max);
+
+               value = (gint) d_min;
+       }
+
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (edit_widget), value);
+}
+
+static void
+ecepp_spin_fill_component (ECompEditorPropertyPart *property_part,
+                          icalcomponent *component)
+{
+       ECompEditorPropertyPartSpinClass *klass;
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+       gint value;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (klass->ical_new_func != NULL);
+       g_return_if_fail (klass->ical_set_func != NULL);
+
+       value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (edit_widget));
+       prop = icalcomponent_get_first_property (component, klass->ical_prop_kind);
+
+       if (prop) {
+               klass->ical_set_func (prop, value);
+       } else {
+               prop = klass->ical_new_func (value);
+               icalcomponent_add_property (component, prop);
+       }
+}
+
+static void
+e_comp_editor_property_part_spin_init (ECompEditorPropertyPartSpin *part_spin)
+{
+       part_spin->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_spin,
+               E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN,
+               ECompEditorPropertyPartSpinPrivate);
+}
+
+static void
+e_comp_editor_property_part_spin_class_init (ECompEditorPropertyPartSpinClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartSpinPrivate));
+
+       klass->ical_prop_kind = ICAL_NO_PROPERTY;
+       klass->ical_new_func = NULL;
+       klass->ical_set_func = NULL;
+       klass->ical_get_func = NULL;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_spin_create_widgets;
+       part_class->fill_widget = ecepp_spin_fill_widget;
+       part_class->fill_component = ecepp_spin_fill_component;
+}
+
+void
+e_comp_editor_property_part_spin_set_range (ECompEditorPropertyPartSpin *part_spin,
+                                           gint min_value,
+                                           gint max_value)
+{
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (part_spin));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_spin));
+       g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
+
+       gtk_spin_button_set_range (GTK_SPIN_BUTTON (edit_widget), min_value, max_value);
+}
+
+void
+e_comp_editor_property_part_spin_get_range (ECompEditorPropertyPartSpin *part_spin,
+                                           gint *out_min_value,
+                                           gint *out_max_value)
+{
+       GtkWidget *edit_widget;
+       gdouble d_min = 0, d_max = 0;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (part_spin));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_spin));
+       g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
+
+       gtk_spin_button_get_range (GTK_SPIN_BUTTON (edit_widget), &d_min, &d_max);
+
+       if (out_min_value)
+               *out_min_value = (gint) d_min;
+       if (out_max_value)
+               *out_max_value = (gint) d_max;
+}
+
+/* ************************************************************************* */
+
+struct _ECompEditorPropertyPartPickerPrivate {
+       gint dummy;
+};
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartPicker, e_comp_editor_property_part_picker, 
E_TYPE_COMP_EDITOR_PROPERTY_PART)
+
+static void
+ecepp_picker_create_widgets (ECompEditorPropertyPart *property_part,
+                            GtkWidget **out_label_widget,
+                            GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartPickerClass *klass;
+       GtkComboBoxText *combo_box;
+       GSList *ids = NULL, *display_names = NULL, *i_link, *dn_link;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (property_part);
+       g_return_if_fail (klass != NULL);
+
+       /* The descendant sets the 'out_label_widget' parameter */
+       *out_edit_widget = gtk_combo_box_text_new ();
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       g_object_set (G_OBJECT (*out_edit_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       gtk_widget_show (*out_edit_widget);
+
+       e_comp_editor_property_part_picker_get_values (E_COMP_EDITOR_PROPERTY_PART_PICKER (property_part),
+               &ids, &display_names);
+
+       g_warn_if_fail (g_slist_length (ids) == g_slist_length (display_names));
+
+       combo_box = GTK_COMBO_BOX_TEXT (*out_edit_widget);
+
+       for (i_link = ids, dn_link = display_names; i_link && dn_link; i_link = g_slist_next (i_link), 
dn_link = g_slist_next (dn_link)) {
+               const gchar *id, *display_name;
+
+               id = i_link->data;
+               display_name = dn_link->data;
+
+               g_warn_if_fail (id != NULL);
+               g_warn_if_fail (display_name != NULL);
+
+               if (!id || !display_name)
+                       continue;
+
+               gtk_combo_box_text_append (combo_box, id, display_name);
+       }
+
+       g_slist_free_full (ids, g_free);
+       g_slist_free_full (display_names, g_free);
+
+       g_signal_connect_swapped (*out_edit_widget, "changed",
+               G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+}
+
+static void
+ecepp_picker_fill_widget (ECompEditorPropertyPart *property_part,
+                         icalcomponent *component)
+{
+       GtkWidget *edit_widget;
+       gchar *id = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget));
+
+       if (e_comp_editor_property_part_picker_get_from_component (
+               E_COMP_EDITOR_PROPERTY_PART_PICKER (property_part),
+               component, &id) && id) {
+               gtk_combo_box_set_active_id (GTK_COMBO_BOX (edit_widget), id);
+               g_free (id);
+       } else {
+               gtk_combo_box_set_active (GTK_COMBO_BOX (edit_widget), 0);
+       }
+}
+
+static void
+ecepp_picker_fill_component (ECompEditorPropertyPart *property_part,
+                            icalcomponent *component)
+{
+       GtkWidget *edit_widget;
+       const gchar *id;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (property_part));
+       g_return_if_fail (component != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget));
+
+       id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (edit_widget));
+       g_return_if_fail (id != NULL);
+
+       e_comp_editor_property_part_picker_set_to_component (
+               E_COMP_EDITOR_PROPERTY_PART_PICKER (property_part),
+               id, component);
+}
+
+static void
+e_comp_editor_property_part_picker_init (ECompEditorPropertyPartPicker *part_picker)
+{
+       part_picker->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_picker,
+               E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER,
+               ECompEditorPropertyPartPickerPrivate);
+}
+
+static void
+e_comp_editor_property_part_picker_class_init (ECompEditorPropertyPartPickerClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartPickerPrivate));
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_picker_create_widgets;
+       part_class->fill_widget = ecepp_picker_fill_widget;
+       part_class->fill_component = ecepp_picker_fill_component;
+}
+
+/* Both out_ids and out_display_names contain newly allocated strings,
+   where also n-th element of out_uids corresponds to n-th element of
+   the out_display_names. */
+void
+e_comp_editor_property_part_picker_get_values (ECompEditorPropertyPartPicker *part_picker,
+                                              GSList **out_ids,
+                                              GSList **out_display_names)
+{
+       ECompEditorPropertyPartPickerClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (part_picker);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->get_values != NULL);
+
+       klass->get_values (part_picker, out_ids, out_display_names);
+}
+
+gboolean
+e_comp_editor_property_part_picker_get_from_component (ECompEditorPropertyPartPicker *part_picker,
+                                                      icalcomponent *component,
+                                                      gchar **out_id)
+{
+       ECompEditorPropertyPartPickerClass *klass;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker), FALSE);
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (part_picker);
+       g_return_val_if_fail (klass != NULL, FALSE);
+       g_return_val_if_fail (klass->get_from_component != NULL, FALSE);
+
+       return klass->get_from_component (part_picker, component, out_id);
+}
+
+void
+e_comp_editor_property_part_picker_set_to_component (ECompEditorPropertyPartPicker *part_picker,
+                                                    const gchar *id,
+                                                    icalcomponent *component)
+{
+       ECompEditorPropertyPartPickerClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker));
+
+       klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (part_picker);
+       g_return_if_fail (klass != NULL);
+       g_return_if_fail (klass->set_to_component != NULL);
+
+       klass->set_to_component (part_picker, id, component);
+}
+
+const gchar *
+e_comp_editor_property_part_picker_get_selected_id (ECompEditorPropertyPartPicker *part_picker)
+{
+       GtkWidget *edit_widget;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker), NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (
+               E_COMP_EDITOR_PROPERTY_PART (part_picker));
+       g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget), NULL);
+
+       return gtk_combo_box_get_active_id (GTK_COMBO_BOX (edit_widget));
+}
+
+void
+e_comp_editor_property_part_picker_set_selected_id (ECompEditorPropertyPartPicker *part_picker,
+                                                   const gchar *id)
+{
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker));
+       g_return_if_fail (id != NULL);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (
+               E_COMP_EDITOR_PROPERTY_PART (part_picker));
+       g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget));
+
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (edit_widget), id);
+}
+
+/* ************************************************************************* */
+
+struct _ECompEditorPropertyPartPickerWithMapPrivate {
+       ECompEditorPropertyPartPickerMap *map;
+       gint n_map_elems;
+       gchar *label;
+
+       icalproperty_kind ical_prop_kind;
+       ECompEditorPropertyPartPickerMapICalNewFunc ical_new_func;
+       ECompEditorPropertyPartPickerMapICalSetFunc ical_set_func;
+       ECompEditorPropertyPartPickerMapICalGetFunc ical_get_func;
+};
+
+enum {
+       PICKER_WITH_MAP_PROP_0,
+       PICKER_WITH_MAP_PROP_MAP,
+       PICKER_WITH_MAP_PROP_LABEL
+};
+
+G_DEFINE_TYPE (ECompEditorPropertyPartPickerWithMap, e_comp_editor_property_part_picker_with_map, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER)
+
+static void
+ecepp_picker_with_map_get_values (ECompEditorPropertyPartPicker *part_picker,
+                                 GSList **out_ids,
+                                 GSList **out_display_names)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
+       gint ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker));
+       g_return_if_fail (out_ids != NULL);
+       g_return_if_fail (out_display_names != NULL);
+
+       part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker);
+       g_return_if_fail (part_picker_with_map->priv->map != NULL);
+       g_return_if_fail (part_picker_with_map->priv->n_map_elems > 0);
+
+       for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
+               *out_ids = g_slist_prepend (*out_ids, g_strdup_printf ("%d", ii));
+               *out_display_names = g_slist_prepend (*out_display_names,
+                       g_strdup (part_picker_with_map->priv->map[ii].description));
+       }
+
+       *out_ids = g_slist_reverse (*out_ids);
+       *out_display_names = g_slist_reverse (*out_display_names);
+}
+
+static gboolean
+ecepp_picker_with_map_get_from_component (ECompEditorPropertyPartPicker *part_picker,
+                                         icalcomponent *component,
+                                         gchar **out_id)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
+       icalproperty *prop;
+       gint ii, value;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+       g_return_val_if_fail (out_id != NULL, FALSE);
+
+       part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker);
+       g_return_val_if_fail (part_picker_with_map->priv->map != NULL, FALSE);
+       g_return_val_if_fail (part_picker_with_map->priv->n_map_elems > 0, FALSE);
+       g_return_val_if_fail (part_picker_with_map->priv->ical_prop_kind != ICAL_NO_PROPERTY, FALSE);
+       g_return_val_if_fail (part_picker_with_map->priv->ical_get_func != NULL, FALSE);
+
+       prop = icalcomponent_get_first_property (component, part_picker_with_map->priv->ical_prop_kind);
+       if (!prop)
+               return FALSE;
+
+       value = part_picker_with_map->priv->ical_get_func (prop);
+
+       for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
+               gboolean matches;
+
+               if (part_picker_with_map->priv->map[ii].matches_func) {
+                       matches = part_picker_with_map->priv->map[ii].matches_func 
(part_picker_with_map->priv->map[ii].value, value);
+               } else {
+                       matches = value == part_picker_with_map->priv->map[ii].value;
+               }
+
+               if (matches) {
+                       *out_id = g_strdup_printf ("%d", ii);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+static void
+ecepp_picker_with_map_set_to_component (ECompEditorPropertyPartPicker *part_picker,
+                                       const gchar *id,
+                                       icalcomponent *component)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
+       icalproperty *prop;
+       gint ii, value;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker));
+       g_return_if_fail (id != NULL);
+       g_return_if_fail (component != NULL);
+
+       part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker);
+       g_return_if_fail (part_picker_with_map->priv->map != NULL);
+       g_return_if_fail (part_picker_with_map->priv->n_map_elems > 0);
+       g_return_if_fail (part_picker_with_map->priv->ical_prop_kind != ICAL_NO_PROPERTY);
+       g_return_if_fail (part_picker_with_map->priv->ical_new_func != NULL);
+       g_return_if_fail (part_picker_with_map->priv->ical_set_func != NULL);
+
+       ii = (gint) g_ascii_strtoll (id, NULL, 10);
+       g_return_if_fail (ii >= 0 && ii < part_picker_with_map->priv->n_map_elems);
+
+       prop = icalcomponent_get_first_property (component, part_picker_with_map->priv->ical_prop_kind);
+       value = part_picker_with_map->priv->map[ii].value;
+
+       if (part_picker_with_map->priv->map[ii].delete_prop) {
+               if (prop) {
+                       icalcomponent_remove_property (component, prop);
+                       icalproperty_free (prop);
+               }
+       } else if (prop) {
+               part_picker_with_map->priv->ical_set_func (prop, value);
+       } else {
+               prop = part_picker_with_map->priv->ical_new_func (value);
+               icalcomponent_add_property (component, prop);
+       }
+}
+
+static void
+ecepp_picker_with_map_create_widgets (ECompEditorPropertyPart *property_part,
+                                     GtkWidget **out_label_widget,
+                                     GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
+       ECompEditorPropertyPartClass *part_class;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS 
(e_comp_editor_property_part_picker_with_map_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (property_part);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (part_picker_with_map->priv->label);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+ecepp_picker_with_map_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
+       gint ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (object));
+
+       part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (object);
+
+       switch (property_id) {
+               case PICKER_WITH_MAP_PROP_MAP:
+                       g_return_if_fail (part_picker_with_map->priv->map == NULL);
+
+                       part_picker_with_map->priv->map = g_value_get_pointer (value);
+                       for (ii = 0; part_picker_with_map->priv->map[ii].description; ii++) {
+                               /* pre-count elements */
+                       }
+
+                       part_picker_with_map->priv->n_map_elems = ii;
+                       return;
+
+               case PICKER_WITH_MAP_PROP_LABEL:
+                       g_free (part_picker_with_map->priv->label);
+                       part_picker_with_map->priv->label = g_value_dup_string (value);
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecepp_picker_with_map_finalize (GObject *object)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map =
+               E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (object);
+
+       if (part_picker_with_map->priv->map && part_picker_with_map->priv->n_map_elems > 0) {
+               gint ii;
+
+               for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
+                       g_free ((gchar *) part_picker_with_map->priv->map[ii].description);
+               }
+
+               g_free (part_picker_with_map->priv->map);
+               part_picker_with_map->priv->map = NULL;
+       }
+
+       g_free (part_picker_with_map->priv->label);
+       part_picker_with_map->priv->label = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_property_part_picker_with_map_parent_class)->finalize (object);
+}
+
+static void
+e_comp_editor_property_part_picker_with_map_init (ECompEditorPropertyPartPickerWithMap *part_picker_with_map)
+{
+       part_picker_with_map->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_picker_with_map,
+               E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP,
+               ECompEditorPropertyPartPickerWithMapPrivate);
+}
+
+static void
+e_comp_editor_property_part_picker_with_map_class_init (ECompEditorPropertyPartPickerWithMapClass *klass)
+{
+       ECompEditorPropertyPartPickerClass *part_picker_class;
+       ECompEditorPropertyPartClass *part_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartPickerWithMapPrivate));
+
+       part_picker_class = E_COMP_EDITOR_PROPERTY_PART_PICKER_CLASS (klass);
+       part_picker_class->get_values = ecepp_picker_with_map_get_values;
+       part_picker_class->get_from_component = ecepp_picker_with_map_get_from_component;
+       part_picker_class->set_to_component = ecepp_picker_with_map_set_to_component;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_picker_with_map_create_widgets;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = ecepp_picker_with_map_set_property;
+       object_class->finalize = ecepp_picker_with_map_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               PICKER_WITH_MAP_PROP_MAP,
+               g_param_spec_pointer (
+                       "map",
+                       "Map",
+                       "Map of values, .description-NULL-terminated",
+                       G_PARAM_WRITABLE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PICKER_WITH_MAP_PROP_LABEL,
+               g_param_spec_string (
+                       "label",
+                       "Label",
+                       "Label of the picker",
+                       NULL,
+                       G_PARAM_WRITABLE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_picker_with_map_new (const ECompEditorPropertyPartPickerMap map[],
+                                                gint n_map_elements,
+                                                const gchar *label,
+                                                icalproperty_kind ical_prop_kind,
+                                                ECompEditorPropertyPartPickerMapICalNewFunc ical_new_func,
+                                                ECompEditorPropertyPartPickerMapICalSetFunc ical_set_func,
+                                                ECompEditorPropertyPartPickerMapICalGetFunc ical_get_func)
+{
+       ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
+       ECompEditorPropertyPartPickerMap *map_copy;
+       ECompEditorPropertyPart *property_part;
+       gint ii;
+
+       g_return_val_if_fail (map != NULL, NULL);
+       g_return_val_if_fail (n_map_elements > 0, NULL);
+       g_return_val_if_fail (label != NULL, NULL);
+       g_return_val_if_fail (ical_prop_kind != ICAL_NO_PROPERTY, NULL);
+       g_return_val_if_fail (ical_new_func != NULL, NULL);
+       g_return_val_if_fail (ical_set_func != NULL, NULL);
+       g_return_val_if_fail (ical_get_func != NULL, NULL);
+
+       map_copy = g_new0 (ECompEditorPropertyPartPickerMap, n_map_elements + 1);
+       for (ii = 0; ii < n_map_elements; ii++) {
+               map_copy[ii] = map[ii];
+               map_copy[ii].description = g_strdup (map[ii].description);
+       }
+
+       property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP,
+               "map", map_copy,
+               "label", label,
+               NULL);
+
+       part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (property_part);
+
+       part_picker_with_map->priv->ical_prop_kind = ical_prop_kind;
+       part_picker_with_map->priv->ical_new_func = ical_new_func;
+       part_picker_with_map->priv->ical_set_func = ical_set_func;
+       part_picker_with_map->priv->ical_get_func = ical_get_func;
+
+       return property_part;
+}
+
+gint
+e_comp_editor_property_part_picker_with_map_get_selected (ECompEditorPropertyPartPickerWithMap 
*part_picker_with_map)
+{
+       gint ii;
+       const gchar *id;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker_with_map), -1);
+       g_return_val_if_fail (part_picker_with_map->priv->map != NULL, -1);
+
+       id = e_comp_editor_property_part_picker_get_selected_id (E_COMP_EDITOR_PROPERTY_PART_PICKER 
(part_picker_with_map));
+       if (!id)
+               return -1;
+
+       ii = (gint) g_ascii_strtoll (id, NULL, 10);
+       if (ii < 0 || ii >= part_picker_with_map->priv->n_map_elems)
+               return -1;
+
+       return part_picker_with_map->priv->map[ii].value;
+}
+
+void
+e_comp_editor_property_part_picker_with_map_set_selected (ECompEditorPropertyPartPickerWithMap 
*part_picker_with_map,
+                                                         gint value)
+{
+       gint ii;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker_with_map));
+       g_return_if_fail (part_picker_with_map->priv->map != NULL);
+
+       for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
+               if (part_picker_with_map->priv->map[ii].value == value) {
+                       gchar *id;
+
+                       id = g_strdup_printf ("%d", ii);
+                       e_comp_editor_property_part_picker_set_selected_id (
+                               E_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker_with_map), id);
+                       g_free (id);
+
+                       return;
+               }
+       }
+
+       g_warn_if_reached ();
+}
+
+/* ************************************************************************* */
diff --git a/calendar/gui/e-comp-editor-property-part.h b/calendar/gui/e-comp-editor-property-part.h
new file mode 100644
index 0000000..35895b7
--- /dev/null
+++ b/calendar/gui/e-comp-editor-property-part.h
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PROPERTY_PART_H
+#define E_COMP_EDITOR_PROPERTY_PART_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <libecal/libecal.h>
+#include <calendar/gui/e-timezone-entry.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART \
+       (e_comp_editor_property_part_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART, ECompEditorPropertyPart))
+#define E_COMP_EDITOR_PROPERTY_PART_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART, ECompEditorPropertyPartClass))
+#define E_IS_COMP_EDITOR_PROPERTY_PART(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART))
+#define E_COMP_EDITOR_PROPERTY_PART_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART, ECompEditorPropertyPartClass))
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING \
+       (e_comp_editor_property_part_string_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_STRING(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING, ECompEditorPropertyPartString))
+#define E_COMP_EDITOR_PROPERTY_PART_STRING_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING, ECompEditorPropertyPartStringClass))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_STRING(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_STRING_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING))
+#define E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING, ECompEditorPropertyPartStringClass))
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME \
+       (e_comp_editor_property_part_datetime_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_DATETIME(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME, ECompEditorPropertyPartDatetime))
+#define E_COMP_EDITOR_PROPERTY_PART_DATETIME_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME, ECompEditorPropertyPartDatetimeClass))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME))
+#define E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME, ECompEditorPropertyPartDatetimeClass))
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN \
+       (e_comp_editor_property_part_spin_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_SPIN(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN, ECompEditorPropertyPartSpin))
+#define E_COMP_EDITOR_PROPERTY_PART_SPIN_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN, ECompEditorPropertyPartSpinClass))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_SPIN(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_SPIN_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN))
+#define E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN, ECompEditorPropertyPartSpinClass))
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER \
+       (e_comp_editor_property_part_picker_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_PICKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER, ECompEditorPropertyPartPicker))
+#define E_COMP_EDITOR_PROPERTY_PART_PICKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER, ECompEditorPropertyPartPickerClass))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_PICKER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER))
+#define E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER, ECompEditorPropertyPartPickerClass))
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP \
+       (e_comp_editor_property_part_picker_with_map_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP, ECompEditorPropertyPartPickerWithMap))
+#define E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP, ECompEditorPropertyPartPickerWithMapClass))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP))
+#define E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP, ECompEditorPropertyPartPickerWithMapClass))
+
+/* ************************************************************************* */
+
+G_BEGIN_DECLS
+
+typedef struct _ECompEditorPropertyPart ECompEditorPropertyPart;
+typedef struct _ECompEditorPropertyPartClass ECompEditorPropertyPartClass;
+typedef struct _ECompEditorPropertyPartPrivate ECompEditorPropertyPartPrivate;
+
+struct _ECompEditorPropertyPart {
+       GObject parent;
+
+       ECompEditorPropertyPartPrivate *priv;
+};
+
+struct _ECompEditorPropertyPartClass {
+       GObjectClass parent_class;
+
+       /* Virtual functions */
+       void            (* create_widgets)      (ECompEditorPropertyPart *property_part,
+                                                GtkWidget **out_label_widget,
+                                                GtkWidget **out_edit_widget);
+       void            (* fill_widget)         (ECompEditorPropertyPart *property_part,
+                                                icalcomponent *component);
+       void            (* fill_component)      (ECompEditorPropertyPart *property_part,
+                                                icalcomponent *component);
+
+       /* Signals */
+       void            (* changed)             (ECompEditorPropertyPart *property_part);
+};
+
+GType          e_comp_editor_property_part_get_type            (void) G_GNUC_CONST;
+gboolean       e_comp_editor_property_part_get_visible         (ECompEditorPropertyPart *property_part);
+void           e_comp_editor_property_part_set_visible         (ECompEditorPropertyPart *property_part,
+                                                                gboolean visible);
+void           e_comp_editor_property_part_create_widgets      (ECompEditorPropertyPart *property_part,
+                                                                GtkWidget **out_label_widget,
+                                                                GtkWidget **out_edit_widget);
+GtkWidget *    e_comp_editor_property_part_get_label_widget    (ECompEditorPropertyPart *property_part);
+GtkWidget *    e_comp_editor_property_part_get_edit_widget     (ECompEditorPropertyPart *property_part);
+void           e_comp_editor_property_part_fill_widget         (ECompEditorPropertyPart *property_part,
+                                                                icalcomponent *component);
+void           e_comp_editor_property_part_fill_component      (ECompEditorPropertyPart *property_part,
+                                                                icalcomponent *component);
+void           e_comp_editor_property_part_emit_changed        (ECompEditorPropertyPart *property_part);
+
+/* ************************************************************************* */
+
+typedef struct _ECompEditorPropertyPartString ECompEditorPropertyPartString;
+typedef struct _ECompEditorPropertyPartStringClass ECompEditorPropertyPartStringClass;
+typedef struct _ECompEditorPropertyPartStringPrivate ECompEditorPropertyPartStringPrivate;
+
+struct _ECompEditorPropertyPartString {
+       ECompEditorPropertyPart parent;
+
+       ECompEditorPropertyPartStringPrivate *priv;
+};
+
+struct _ECompEditorPropertyPartStringClass {
+       ECompEditorPropertyPartClass parent_class;
+
+       /* What entry GType (derived from GtkEntry or GtkTextView) should be used;
+          the default is the GtkEntry */
+       GType entry_type;
+
+       /* ical property kind and its manipulation functions */
+       icalproperty_kind ical_prop_kind;
+       icalproperty *  (* ical_new_func)       (const gchar *value);
+       void            (* ical_set_func)       (icalproperty *prop,
+                                                const gchar *value);
+       const gchar *   (* ical_get_func)       (const icalproperty *prop);
+};
+
+GType          e_comp_editor_property_part_string_get_type     (void) G_GNUC_CONST;
+void           e_comp_editor_property_part_string_attach_focus_tracker
+                                                               (ECompEditorPropertyPartString *part_string,
+                                                                EFocusTracker *focus_tracker);
+
+/* ************************************************************************* */
+
+typedef struct _ECompEditorPropertyPartDatetime ECompEditorPropertyPartDatetime;
+typedef struct _ECompEditorPropertyPartDatetimeClass ECompEditorPropertyPartDatetimeClass;
+typedef struct _ECompEditorPropertyPartDatetimePrivate ECompEditorPropertyPartDatetimePrivate;
+
+struct _ECompEditorPropertyPartDatetime {
+       ECompEditorPropertyPart parent;
+
+       ECompEditorPropertyPartDatetimePrivate *priv;
+};
+
+struct _ECompEditorPropertyPartDatetimeClass {
+       ECompEditorPropertyPartClass parent_class;
+
+       /* ical property kind and its manipulation functions */
+       icalproperty_kind ical_prop_kind;
+       icalproperty *  (* ical_new_func)       (struct icaltimetype value);
+       void            (* ical_set_func)       (icalproperty *prop,
+                                                struct icaltimetype value);
+       struct icaltimetype
+                       (* ical_get_func)       (const icalproperty *prop);
+};
+
+GType          e_comp_editor_property_part_datetime_get_type   (void) G_GNUC_CONST;
+void           e_comp_editor_property_part_datetime_attach_timezone_entry
+                                                               (ECompEditorPropertyPartDatetime 
*part_datetime,
+                                                                ETimezoneEntry *timezone_entry);
+void           e_comp_editor_property_part_datetime_set_date_only
+                                                               (ECompEditorPropertyPartDatetime 
*part_datetime,
+                                                                gboolean date_only);
+gboolean       e_comp_editor_property_part_datetime_get_date_only
+                                                               (ECompEditorPropertyPartDatetime 
*part_datetime);
+void           e_comp_editor_property_part_datetime_set_allow_no_date_set
+                                                               (ECompEditorPropertyPartDatetime 
*part_datetime,
+                                                                gboolean allow_no_date_set);
+gboolean       e_comp_editor_property_part_datetime_get_allow_no_date_set
+                                                               (ECompEditorPropertyPartDatetime 
*part_datetime);
+void           e_comp_editor_property_part_datetime_set_value  (ECompEditorPropertyPartDatetime 
*part_datetime,
+                                                                struct icaltimetype value);
+struct icaltimetype
+               e_comp_editor_property_part_datetime_get_value  (ECompEditorPropertyPartDatetime 
*part_datetime);
+gboolean       e_comp_editor_property_part_datetime_check_validity
+                                                               (ECompEditorPropertyPartDatetime 
*part_datetime,
+                                                                gboolean *out_date_is_valid,
+                                                                gboolean *out_time_is_valid);
+
+/* ************************************************************************* */
+
+typedef struct _ECompEditorPropertyPartSpin ECompEditorPropertyPartSpin;
+typedef struct _ECompEditorPropertyPartSpinClass ECompEditorPropertyPartSpinClass;
+typedef struct _ECompEditorPropertyPartSpinPrivate ECompEditorPropertyPartSpinPrivate;
+
+struct _ECompEditorPropertyPartSpin {
+       ECompEditorPropertyPart parent;
+
+       ECompEditorPropertyPartSpinPrivate *priv;
+};
+
+struct _ECompEditorPropertyPartSpinClass {
+       ECompEditorPropertyPartClass parent_class;
+
+       /* ical property kind and its manipulation functions */
+       icalproperty_kind ical_prop_kind;
+       icalproperty *  (* ical_new_func)       (gint value);
+       void            (* ical_set_func)       (icalproperty *prop,
+                                                gint value);
+       gint            (* ical_get_func)       (const icalproperty *prop);
+};
+
+GType          e_comp_editor_property_part_spin_get_type       (void) G_GNUC_CONST;
+void           e_comp_editor_property_part_spin_set_range      (ECompEditorPropertyPartSpin *part_spin,
+                                                                gint min_value,
+                                                                gint max_value);
+void           e_comp_editor_property_part_spin_get_range      (ECompEditorPropertyPartSpin *part_spin,
+                                                                gint *out_min_value,
+                                                                gint *out_max_value);
+
+/* ************************************************************************* */
+
+typedef struct _ECompEditorPropertyPartPicker ECompEditorPropertyPartPicker;
+typedef struct _ECompEditorPropertyPartPickerClass ECompEditorPropertyPartPickerClass;
+typedef struct _ECompEditorPropertyPartPickerPrivate ECompEditorPropertyPartPickerPrivate;
+
+struct _ECompEditorPropertyPartPicker {
+       ECompEditorPropertyPart parent;
+
+       ECompEditorPropertyPartPickerPrivate *priv;
+};
+
+struct _ECompEditorPropertyPartPickerClass {
+       ECompEditorPropertyPartClass parent_class;
+
+       void            (* get_values)          (ECompEditorPropertyPartPicker *part_picker,
+                                                GSList **out_ids,
+                                                GSList **out_display_names);
+       gboolean        (* get_from_component)  (ECompEditorPropertyPartPicker *part_picker,
+                                                icalcomponent *component,
+                                                gchar **out_id);
+       void            (* set_to_component)    (ECompEditorPropertyPartPicker *part_picker,
+                                                const gchar *id,
+                                                icalcomponent *component);
+};
+
+GType          e_comp_editor_property_part_picker_get_type     (void) G_GNUC_CONST;
+void           e_comp_editor_property_part_picker_get_values   (ECompEditorPropertyPartPicker *part_picker,
+                                                                GSList **out_ids,
+                                                                GSList **out_display_names);
+gboolean       e_comp_editor_property_part_picker_get_from_component
+                                                               (ECompEditorPropertyPartPicker *part_picker,
+                                                                icalcomponent *component,
+                                                                gchar **out_id);
+void           e_comp_editor_property_part_picker_set_to_component
+                                                               (ECompEditorPropertyPartPicker *part_picker,
+                                                                const gchar *id,
+                                                                icalcomponent *component);
+const gchar *  e_comp_editor_property_part_picker_get_selected_id
+                                                               (ECompEditorPropertyPartPicker *part_picker);
+void           e_comp_editor_property_part_picker_set_selected_id
+                                                               (ECompEditorPropertyPartPicker *part_picker,
+                                                                const gchar *id);
+
+/* ************************************************************************* */
+
+typedef struct _ECompEditorPropertyPartPickerWithMap ECompEditorPropertyPartPickerWithMap;
+typedef struct _ECompEditorPropertyPartPickerWithMapClass ECompEditorPropertyPartPickerWithMapClass;
+typedef struct _ECompEditorPropertyPartPickerWithMapPrivate ECompEditorPropertyPartPickerWithMapPrivate;
+
+typedef struct _ECompEditorPropertyPartPickerMap {
+       gint value;               /* libical enum value */
+       const gchar *description; /* user visible description of the value */
+       gboolean delete_prop;     /* whether to delete property from the component when this one is selected 
*/
+       gboolean (*matches_func) (gint map_value, gint component_value); /* can be NULL, then 'equal' compare 
is done */
+} ECompEditorPropertyPartPickerMap;
+
+typedef icalproperty * (* ECompEditorPropertyPartPickerMapICalNewFunc) (gint value);
+typedef void           (* ECompEditorPropertyPartPickerMapICalSetFunc) (icalproperty *prop,
+                                                                        gint value);
+typedef gint           (* ECompEditorPropertyPartPickerMapICalGetFunc) (const icalproperty *prop);
+
+struct _ECompEditorPropertyPartPickerWithMap {
+       ECompEditorPropertyPartPicker parent;
+
+       ECompEditorPropertyPartPickerWithMapPrivate *priv;
+};
+
+struct _ECompEditorPropertyPartPickerWithMapClass {
+       ECompEditorPropertyPartPickerClass parent_class;
+};
+
+GType          e_comp_editor_property_part_picker_with_map_get_type
+                                                               (void) G_GNUC_CONST;
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_picker_with_map_new (const ECompEditorPropertyPartPickerMap map[],
+                                                                gint n_map_elements,
+                                                                const gchar *label,
+                                                                icalproperty_kind ical_prop_kind,
+                                                                ECompEditorPropertyPartPickerMapICalNewFunc 
ical_new_func,
+                                                                ECompEditorPropertyPartPickerMapICalSetFunc 
ical_set_func,
+                                                                ECompEditorPropertyPartPickerMapICalGetFunc 
ical_get_func);
+gint           e_comp_editor_property_part_picker_with_map_get_selected
+                                                               (ECompEditorPropertyPartPickerWithMap 
*part_picker_with_map);
+void           e_comp_editor_property_part_picker_with_map_set_selected
+                                                               (ECompEditorPropertyPartPickerWithMap 
*part_picker_with_map,
+                                                                gint value);
+
+/* ************************************************************************* */
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PROPERTY_PART_H */
diff --git a/calendar/gui/e-comp-editor-property-parts.c b/calendar/gui/e-comp-editor-property-parts.c
new file mode 100644
index 0000000..6dcf24a
--- /dev/null
+++ b/calendar/gui/e-comp-editor-property-parts.c
@@ -0,0 +1,1553 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/libedataserver.h>
+#include <e-util/e-util.h>
+
+#include "calendar-config.h"
+#include "e-timezone-entry.h"
+
+#include "e-comp-editor-property-part.h"
+#include "e-comp-editor-property-parts.h"
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_SUMMARY \
+       (e_comp_editor_property_part_summary_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_SUMMARY(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_SUMMARY, ECompEditorPropertyPartSummary))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_SUMMARY(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_SUMMARY))
+
+typedef struct _ECompEditorPropertyPartSummary ECompEditorPropertyPartSummary;
+typedef struct _ECompEditorPropertyPartSummaryClass ECompEditorPropertyPartSummaryClass;
+
+struct _ECompEditorPropertyPartSummary {
+       ECompEditorPropertyPartString parent;
+};
+
+struct _ECompEditorPropertyPartSummaryClass {
+       ECompEditorPropertyPartStringClass parent_class;
+};
+
+GType e_comp_editor_property_part_summary_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartSummary, e_comp_editor_property_part_summary, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING)
+
+static void
+ecepp_summary_create_widgets (ECompEditorPropertyPart *property_part,
+                             GtkWidget **out_label_widget,
+                             GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SUMMARY (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_summary_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "_Summary:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+e_comp_editor_property_part_summary_init (ECompEditorPropertyPartSummary *part_summary)
+{
+}
+
+static void
+e_comp_editor_property_part_summary_class_init (ECompEditorPropertyPartSummaryClass *klass)
+{
+       ECompEditorPropertyPartStringClass *part_string_class;
+       ECompEditorPropertyPartClass *part_class;
+
+       part_string_class = E_COMP_EDITOR_PROPERTY_PART_STRING_CLASS (klass);
+       part_string_class->entry_type = E_TYPE_SPELL_ENTRY;
+       part_string_class->ical_prop_kind = ICAL_SUMMARY_PROPERTY;
+       part_string_class->ical_new_func = icalproperty_new_summary;
+       part_string_class->ical_set_func = icalproperty_set_summary;
+       part_string_class->ical_get_func = icalproperty_get_summary;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_summary_create_widgets;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_summary_new (EFocusTracker *focus_tracker)
+{
+       ECompEditorPropertyPart *property_part;
+
+       property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_SUMMARY, NULL);
+
+       e_comp_editor_property_part_string_attach_focus_tracker (
+               E_COMP_EDITOR_PROPERTY_PART_STRING (property_part), focus_tracker);
+
+       return property_part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_LOCATION \
+       (e_comp_editor_property_part_location_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_LOCATION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_LOCATION, ECompEditorPropertyPartLocation))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_LOCATION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_LOCATION))
+
+typedef struct _ECompEditorPropertyPartLocation ECompEditorPropertyPartLocation;
+typedef struct _ECompEditorPropertyPartLocationClass ECompEditorPropertyPartLocationClass;
+
+struct _ECompEditorPropertyPartLocation {
+       ECompEditorPropertyPartString parent;
+};
+
+struct _ECompEditorPropertyPartLocationClass {
+       ECompEditorPropertyPartStringClass parent_class;
+};
+
+GType e_comp_editor_property_part_location_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartLocation, e_comp_editor_property_part_location, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING)
+
+static gchar *
+ecepp_location_get_locations_filename (gboolean config_dir_only)
+{
+       return g_build_filename (e_get_user_config_dir (), "calendar", config_dir_only ? NULL : "locations", 
NULL);
+}
+
+static void
+ecepp_location_load_list (GtkEntry *entry)
+{
+       GtkEntryCompletion *completion;
+       GtkListStore *store;
+       gchar *filename, *contents = NULL;
+       gchar **locations;
+       gint row;
+       GError *error = NULL;
+
+       g_return_if_fail (GTK_IS_ENTRY (entry));
+
+       completion = gtk_entry_get_completion (entry);
+       g_return_if_fail (completion != NULL);
+
+       filename = ecepp_location_get_locations_filename (FALSE);
+
+       if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+               g_free (filename);
+               return;
+       }
+
+       if (!g_file_get_contents (filename, &contents, NULL, &error)) {
+               if (error != NULL) {
+                       g_warning (
+                               "%s: Failed to load locations list '%s': %s",
+                               G_STRFUNC, filename, error->message);
+                       g_error_free (error);
+               }
+
+               g_free (filename);
+               return;
+       }
+
+       locations = g_strsplit (contents, "\n", 0);
+       if (!locations) {
+               g_free (contents);
+               g_free (filename);
+               return;
+       }
+
+       row = 0;
+       store = GTK_LIST_STORE (gtk_entry_completion_get_model (completion));
+       while (locations[row] && *locations[row]) {
+               GtkTreeIter iter;
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, locations[row], -1);
+               row++;
+       }
+
+       g_strfreev (locations);
+       g_free (contents);
+       g_free (filename);
+}
+
+static void
+ecepp_location_save_list (GtkEntry *entry)
+{
+       GtkEntryCompletion *completion;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       const gchar *current_location;
+       gchar *filename, *stored_content = NULL;
+       gboolean needs_save = TRUE;
+       GString *contents;
+       GError *error = NULL;
+
+       g_return_if_fail (GTK_IS_ENTRY (entry));
+
+       completion = gtk_entry_get_completion (entry);
+       g_return_if_fail (completion != NULL);
+
+       filename = ecepp_location_get_locations_filename (TRUE);
+
+       if (!g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
+               gint r = g_mkdir_with_parents (filename, 0700);
+               if (r < 0) {
+                       g_warning ("%s: Failed to create %s: %s", G_STRFUNC, filename, g_strerror (errno));
+                       g_free (filename);
+                       return;
+               }
+       }
+
+       g_free (filename);
+
+       filename = ecepp_location_get_locations_filename (FALSE);
+       current_location = gtk_entry_get_text (entry);
+
+       /* Put current locatin on the very top of the list */
+       contents = g_string_new (current_location);
+       if (contents->len > 0)
+               g_string_append_c (contents, '\n');
+
+       model = gtk_entry_completion_get_model (completion);
+       if (gtk_tree_model_get_iter_first (model, &iter)) {
+               gint i = 0;
+               do {
+                       gchar *str;
+
+                       gtk_tree_model_get (model, &iter, 0, &str, -1);
+
+                       /* Skip the current location */
+                       if (str && *str && g_ascii_strcasecmp (str, current_location) != 0)
+                               g_string_append_printf (contents, "%s\n", str);
+
+                       g_free (str);
+
+                       i++;
+
+               } while (gtk_tree_model_iter_next (model, &iter) && (i < 20));
+       }
+
+       if (g_file_get_contents (filename, &stored_content, NULL, NULL)) {
+               needs_save = g_strcmp0 (stored_content, contents->str) != 0;
+               g_free (stored_content);
+       }
+
+       if (needs_save) {
+               g_file_set_contents (filename, contents->str, -1, &error);
+               if (error != NULL) {
+                       g_warning (
+                               "%s: Failed to save locations '%s': %s",
+                               G_STRFUNC, filename, error->message);
+                       g_error_free (error);
+               }
+       }
+
+       g_string_free (contents, TRUE);
+       g_free (filename);
+}
+
+static void
+ecepp_location_create_widgets (ECompEditorPropertyPart *property_part,
+                              GtkWidget **out_label_widget,
+                              GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GtkEntryCompletion *completion;
+       GtkListStore *list_store;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_LOCATION (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_location_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       completion = gtk_entry_completion_new ();
+
+       list_store = gtk_list_store_new (1, G_TYPE_STRING);
+       gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (list_store));
+       gtk_entry_completion_set_text_column (completion, 0);
+
+       gtk_entry_set_completion (GTK_ENTRY (*out_edit_widget), completion);
+       g_object_unref (completion);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "_Location:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+
+       ecepp_location_load_list (GTK_ENTRY (*out_edit_widget));
+}
+
+static void
+ecepp_location_fill_component (ECompEditorPropertyPart *property_part,
+                              icalcomponent *component)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_LOCATION (property_part));
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_location_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->fill_component != NULL);
+
+       part_class->fill_component (property_part, component);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_ENTRY (edit_widget));
+
+       ecepp_location_save_list (GTK_ENTRY (edit_widget));
+}
+
+static void
+e_comp_editor_property_part_location_init (ECompEditorPropertyPartLocation *part_location)
+{
+}
+
+static void
+e_comp_editor_property_part_location_class_init (ECompEditorPropertyPartLocationClass *klass)
+{
+       ECompEditorPropertyPartStringClass *part_string_class;
+       ECompEditorPropertyPartClass *part_class;
+
+       part_string_class = E_COMP_EDITOR_PROPERTY_PART_STRING_CLASS (klass);
+       part_string_class->ical_prop_kind = ICAL_LOCATION_PROPERTY;
+       part_string_class->ical_new_func = icalproperty_new_location;
+       part_string_class->ical_set_func = icalproperty_set_location;
+       part_string_class->ical_get_func = icalproperty_get_location;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_location_create_widgets;
+       part_class->fill_component = ecepp_location_fill_component;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_location_new (EFocusTracker *focus_tracker)
+{
+       ECompEditorPropertyPart *property_part;
+
+       property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_LOCATION, NULL);
+
+       e_comp_editor_property_part_string_attach_focus_tracker (
+               E_COMP_EDITOR_PROPERTY_PART_STRING (property_part), focus_tracker);
+
+       return property_part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_CATEGORIES \
+       (e_comp_editor_property_part_categories_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_CATEGORIES(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_CATEGORIES, ECompEditorPropertyPartCategories))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_CATEGORIES(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_CATEGORIES))
+
+typedef struct _ECompEditorPropertyPartCategories ECompEditorPropertyPartCategories;
+typedef struct _ECompEditorPropertyPartCategoriesClass ECompEditorPropertyPartCategoriesClass;
+
+struct _ECompEditorPropertyPartCategories {
+       ECompEditorPropertyPartString parent;
+};
+
+struct _ECompEditorPropertyPartCategoriesClass {
+       ECompEditorPropertyPartStringClass parent_class;
+};
+
+GType e_comp_editor_property_part_categories_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartCategories, e_comp_editor_property_part_categories, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING)
+
+static void
+ecepp_categories_button_clicked_cb (GtkButton *button,
+                                   GtkEntry *entry)
+{
+       g_return_if_fail (GTK_IS_ENTRY (entry));
+
+       e_categories_config_open_dialog_for_entry (entry);
+}
+
+static void
+ecepp_categories_create_widgets (ECompEditorPropertyPart *property_part,
+                                GtkWidget **out_label_widget,
+                                GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GtkEntryCompletion *completion;
+       GtkWidget *button;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_CATEGORIES (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_categories_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       completion = e_category_completion_new ();
+       gtk_entry_set_completion (GTK_ENTRY (*out_edit_widget), completion);
+       g_object_unref (completion);
+
+       button = gtk_button_new_with_mnemonic (C_("ECompEditor", "_Categories..."));
+       g_signal_connect (button, "clicked", G_CALLBACK (ecepp_categories_button_clicked_cb), 
*out_edit_widget);
+
+       *out_label_widget = button;
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+e_comp_editor_property_part_categories_init (ECompEditorPropertyPartCategories *part_categories)
+{
+}
+
+static void
+e_comp_editor_property_part_categories_class_init (ECompEditorPropertyPartCategoriesClass *klass)
+{
+       ECompEditorPropertyPartStringClass *part_string_class;
+       ECompEditorPropertyPartClass *part_class;
+
+       part_string_class = E_COMP_EDITOR_PROPERTY_PART_STRING_CLASS (klass);
+       part_string_class->ical_prop_kind = ICAL_CATEGORIES_PROPERTY;
+       part_string_class->ical_new_func = icalproperty_new_categories;
+       part_string_class->ical_set_func = icalproperty_set_categories;
+       part_string_class->ical_get_func = icalproperty_get_categories;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_categories_create_widgets;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_categories_new (EFocusTracker *focus_tracker)
+{
+       ECompEditorPropertyPart *property_part;
+
+       property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_CATEGORIES, NULL);
+
+       e_comp_editor_property_part_string_attach_focus_tracker (
+               E_COMP_EDITOR_PROPERTY_PART_STRING (property_part), focus_tracker);
+
+       return property_part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_DESCRIPTION \
+       (e_comp_editor_property_part_description_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_DESCRIPTION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DESCRIPTION, ECompEditorPropertyPartDescription))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DESCRIPTION))
+
+typedef struct _ECompEditorPropertyPartDescription ECompEditorPropertyPartDescription;
+typedef struct _ECompEditorPropertyPartDescriptionClass ECompEditorPropertyPartDescriptionClass;
+
+struct _ECompEditorPropertyPartDescription {
+       ECompEditorPropertyPartString parent;
+};
+
+struct _ECompEditorPropertyPartDescriptionClass {
+       ECompEditorPropertyPartStringClass parent_class;
+};
+
+GType e_comp_editor_property_part_description_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartDescription, e_comp_editor_property_part_description, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING)
+
+static void
+ecepp_description_create_widgets (ECompEditorPropertyPart *property_part,
+                                 GtkWidget **out_label_widget,
+                                 GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GtkTextView *text_view;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_description_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "_Description:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       text_view = GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (*out_edit_widget)));
+       gtk_text_view_set_wrap_mode (text_view, GTK_WRAP_WORD);
+       e_buffer_tagger_connect (text_view);
+       e_spell_text_view_attach (text_view);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       g_object_set (G_OBJECT (*out_edit_widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "height-request", 100,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+ecepp_description_fill_widget (ECompEditorPropertyPart *property_part,
+                              icalcomponent *component)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GtkWidget *edit_widget;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DESCRIPTION (property_part));
+       g_return_if_fail (component != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_description_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->fill_widget != NULL);
+
+       part_class->fill_widget (property_part, component);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_SCROLLED_WINDOW (edit_widget));
+
+       e_buffer_tagger_update_tags (GTK_TEXT_VIEW (gtk_bin_get_child (GTK_BIN (edit_widget))));
+}
+
+static void
+e_comp_editor_property_part_description_init (ECompEditorPropertyPartDescription *part_description)
+{
+}
+
+static void
+e_comp_editor_property_part_description_class_init (ECompEditorPropertyPartDescriptionClass *klass)
+{
+       ECompEditorPropertyPartStringClass *part_string_class;
+       ECompEditorPropertyPartClass *part_class;
+
+       part_string_class = E_COMP_EDITOR_PROPERTY_PART_STRING_CLASS (klass);
+       part_string_class->entry_type = GTK_TYPE_TEXT_VIEW;
+       part_string_class->ical_prop_kind = ICAL_DESCRIPTION_PROPERTY;
+       part_string_class->ical_new_func = icalproperty_new_description;
+       part_string_class->ical_set_func = icalproperty_set_description;
+       part_string_class->ical_get_func = icalproperty_get_description;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_description_create_widgets;
+       part_class->fill_widget = ecepp_description_fill_widget;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_description_new (EFocusTracker *focus_tracker)
+{
+       ECompEditorPropertyPart *property_part;
+
+       property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_DESCRIPTION, NULL);
+
+       e_comp_editor_property_part_string_attach_focus_tracker (
+               E_COMP_EDITOR_PROPERTY_PART_STRING (property_part), focus_tracker);
+
+       return property_part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_URL \
+       (e_comp_editor_property_part_url_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_URL(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_URL, ECompEditorPropertyPartUrl))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_URL(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_URL))
+
+typedef struct _ECompEditorPropertyPartUrl ECompEditorPropertyPartUrl;
+typedef struct _ECompEditorPropertyPartUrlClass ECompEditorPropertyPartUrlClass;
+
+struct _ECompEditorPropertyPartUrl {
+       ECompEditorPropertyPartString parent;
+};
+
+struct _ECompEditorPropertyPartUrlClass {
+       ECompEditorPropertyPartStringClass parent_class;
+};
+
+GType e_comp_editor_property_part_url_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartUrl, e_comp_editor_property_part_url, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING)
+
+static void
+ecepp_url_create_widgets (ECompEditorPropertyPart *property_part,
+                         GtkWidget **out_label_widget,
+                         GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_URL (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_url_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "_Web page:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+e_comp_editor_property_part_url_init (ECompEditorPropertyPartUrl *part_url)
+{
+}
+
+static void
+e_comp_editor_property_part_url_class_init (ECompEditorPropertyPartUrlClass *klass)
+{
+       ECompEditorPropertyPartStringClass *part_string_class;
+       ECompEditorPropertyPartClass *part_class;
+
+       part_string_class = E_COMP_EDITOR_PROPERTY_PART_STRING_CLASS (klass);
+       part_string_class->entry_type = E_TYPE_URL_ENTRY;
+       part_string_class->ical_prop_kind = ICAL_URL_PROPERTY;
+       part_string_class->ical_new_func = icalproperty_new_url;
+       part_string_class->ical_set_func = icalproperty_set_url;
+       part_string_class->ical_get_func = icalproperty_get_url;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_url_create_widgets;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_url_new (EFocusTracker *focus_tracker)
+{
+       ECompEditorPropertyPart *property_part;
+
+       property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_URL, NULL);
+
+       e_comp_editor_property_part_string_attach_focus_tracker (
+               E_COMP_EDITOR_PROPERTY_PART_STRING (property_part), focus_tracker);
+
+       return property_part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED \
+       (e_comp_editor_property_part_datetime_labeled_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED, ECompEditorPropertyPartDatetimeLabeled))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED))
+
+typedef struct _ECompEditorPropertyPartDatetimeLabeled ECompEditorPropertyPartDatetimeLabeled;
+typedef struct _ECompEditorPropertyPartDatetimeLabeledClass ECompEditorPropertyPartDatetimeLabeledClass;
+
+struct _ECompEditorPropertyPartDatetimeLabeled {
+       ECompEditorPropertyPartDatetime parent;
+
+       gchar *label;
+};
+
+struct _ECompEditorPropertyPartDatetimeLabeledClass {
+       ECompEditorPropertyPartDatetimeClass parent_class;
+};
+
+GType e_comp_editor_property_part_datetime_labeled_get_type (void) G_GNUC_CONST;
+
+enum {
+       DATETIME_LABELED_PROP_0,
+       DATETIME_LABELED_PROP_LABEL
+};
+
+G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartDatetimeLabeled, 
e_comp_editor_property_part_datetime_labeled, E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME)
+
+static void
+ecepp_datetime_labeled_create_widgets (ECompEditorPropertyPart *property_part,
+                                      GtkWidget **out_label_widget,
+                                      GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartDatetimeLabeled *part_datetime_labeled;
+       ECompEditorPropertyPartClass *part_class;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS 
(e_comp_editor_property_part_datetime_labeled_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       part_datetime_labeled = E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (property_part);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (part_datetime_labeled->label);
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+ecepp_datetime_labeled_set_property (GObject *object,
+                                    guint property_id,
+                                    const GValue *value,
+                                    GParamSpec *pspec)
+{
+       ECompEditorPropertyPartDatetimeLabeled *part_datetime_labeled;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (object));
+
+       part_datetime_labeled = E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (object);
+
+       switch (property_id) {
+               case DATETIME_LABELED_PROP_LABEL:
+                       g_free (part_datetime_labeled->label);
+                       part_datetime_labeled->label = g_value_dup_string (value);
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ecepp_datetime_labeled_finalize (GObject *object)
+{
+       ECompEditorPropertyPartDatetimeLabeled *part_datetime_labeled;
+
+       part_datetime_labeled = E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (object);
+
+       g_free (part_datetime_labeled->label);
+       part_datetime_labeled->label = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_property_part_datetime_labeled_parent_class)->finalize (object);
+}
+
+static void
+e_comp_editor_property_part_datetime_labeled_init (ECompEditorPropertyPartDatetimeLabeled 
*part_datetime_labeled)
+{
+}
+
+static void
+e_comp_editor_property_part_datetime_labeled_class_init (ECompEditorPropertyPartDatetimeLabeledClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+       GObjectClass *object_class;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_datetime_labeled_create_widgets;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = ecepp_datetime_labeled_set_property;
+       object_class->finalize = ecepp_datetime_labeled_finalize;
+
+       g_object_class_install_property (
+               object_class,
+               DATETIME_LABELED_PROP_LABEL,
+               g_param_spec_string (
+                       "label",
+                       "Label",
+                       "Label of the datetime",
+                       NULL,
+                       G_PARAM_WRITABLE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_comp_editor_property_part_datetime_labeled_setup (ECompEditorPropertyPartDatetimeLabeled 
*part_datetime_labeled,
+                                                   gboolean date_only,
+                                                   gboolean allow_no_date_set)
+{
+       ECompEditorPropertyPartDatetime *part_datetime;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (part_datetime_labeled));
+
+       part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime_labeled);
+
+       e_comp_editor_property_part_datetime_set_date_only (part_datetime, date_only);
+       e_comp_editor_property_part_datetime_set_allow_no_date_set (part_datetime, allow_no_date_set);
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_DTSTART \
+       (e_comp_editor_property_part_dtstart_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_DTSTART(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DTSTART, ECompEditorPropertyPartDtstart))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DTSTART(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DTSTART))
+
+typedef struct _ECompEditorPropertyPartDtstart ECompEditorPropertyPartDtstart;
+typedef struct _ECompEditorPropertyPartDtstartClass ECompEditorPropertyPartDtstartClass;
+
+struct _ECompEditorPropertyPartDtstart {
+       ECompEditorPropertyPartDatetimeLabeled parent;
+};
+
+struct _ECompEditorPropertyPartDtstartClass {
+       ECompEditorPropertyPartDatetimeLabeledClass parent_class;
+};
+
+GType e_comp_editor_property_part_dtstart_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartDtstart, e_comp_editor_property_part_dtstart, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED)
+
+static void
+e_comp_editor_property_part_dtstart_init (ECompEditorPropertyPartDtstart *part_dtstart)
+{
+}
+
+static void
+e_comp_editor_property_part_dtstart_class_init (ECompEditorPropertyPartDtstartClass *klass)
+{
+       ECompEditorPropertyPartDatetimeClass *part_datetime_class;
+
+       part_datetime_class = E_COMP_EDITOR_PROPERTY_PART_DATETIME_CLASS (klass);
+       part_datetime_class->ical_prop_kind = ICAL_DTSTART_PROPERTY;
+       part_datetime_class->ical_new_func = icalproperty_new_dtstart;
+       part_datetime_class->ical_set_func = icalproperty_set_dtstart;
+       part_datetime_class->ical_get_func = icalproperty_get_dtstart;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_dtstart_new (const gchar *label,
+                                        gboolean date_only,
+                                        gboolean allow_no_date_set)
+{
+       ECompEditorPropertyPart *part;
+
+       part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_DTSTART,
+               "label", label,
+               NULL);
+
+       e_comp_editor_property_part_datetime_labeled_setup (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (part),
+               date_only, allow_no_date_set);
+
+       return part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_DTEND \
+       (e_comp_editor_property_part_dtend_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_DTEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DTEND, ECompEditorPropertyPartDtend))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DTEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DTEND))
+
+typedef struct _ECompEditorPropertyPartDtend ECompEditorPropertyPartDtend;
+typedef struct _ECompEditorPropertyPartDtendClass ECompEditorPropertyPartDtendClass;
+
+struct _ECompEditorPropertyPartDtend {
+       ECompEditorPropertyPartDatetimeLabeled parent;
+};
+
+struct _ECompEditorPropertyPartDtendClass {
+       ECompEditorPropertyPartDatetimeLabeledClass parent_class;
+};
+
+GType e_comp_editor_property_part_dtend_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartDtend, e_comp_editor_property_part_dtend, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED)
+
+static void
+e_comp_editor_property_part_dtend_init (ECompEditorPropertyPartDtend *part_dtend)
+{
+}
+
+static void
+e_comp_editor_property_part_dtend_class_init (ECompEditorPropertyPartDtendClass *klass)
+{
+       ECompEditorPropertyPartDatetimeClass *part_datetime_class;
+
+       part_datetime_class = E_COMP_EDITOR_PROPERTY_PART_DATETIME_CLASS (klass);
+       part_datetime_class->ical_prop_kind = ICAL_DTEND_PROPERTY;
+       part_datetime_class->ical_new_func = icalproperty_new_dtend;
+       part_datetime_class->ical_set_func = icalproperty_set_dtend;
+       part_datetime_class->ical_get_func = icalproperty_get_dtend;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_dtend_new (const gchar *label,
+                                      gboolean date_only,
+                                      gboolean allow_no_date_set)
+{
+       ECompEditorPropertyPart *part;
+
+       part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_DTEND,
+               "label", label,
+               NULL);
+
+       e_comp_editor_property_part_datetime_labeled_setup (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (part),
+               date_only, allow_no_date_set);
+
+       return part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_DUE \
+       (e_comp_editor_property_part_due_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_DUE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DUE, ECompEditorPropertyPartDue))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_DUE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_DUE))
+
+typedef struct _ECompEditorPropertyPartDue ECompEditorPropertyPartDue;
+typedef struct _ECompEditorPropertyPartDueClass ECompEditorPropertyPartDueClass;
+
+struct _ECompEditorPropertyPartDue {
+       ECompEditorPropertyPartDatetimeLabeled parent;
+};
+
+struct _ECompEditorPropertyPartDueClass {
+       ECompEditorPropertyPartDatetimeLabeledClass parent_class;
+};
+
+GType e_comp_editor_property_part_due_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartDue, e_comp_editor_property_part_due, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED)
+
+static void
+e_comp_editor_property_part_due_init (ECompEditorPropertyPartDue *part_due)
+{
+}
+
+static void
+e_comp_editor_property_part_due_class_init (ECompEditorPropertyPartDueClass *klass)
+{
+       ECompEditorPropertyPartDatetimeClass *part_datetime_class;
+
+       part_datetime_class = E_COMP_EDITOR_PROPERTY_PART_DATETIME_CLASS (klass);
+       part_datetime_class->ical_prop_kind = ICAL_DUE_PROPERTY;
+       part_datetime_class->ical_new_func = icalproperty_new_due;
+       part_datetime_class->ical_set_func = icalproperty_set_due;
+       part_datetime_class->ical_get_func = icalproperty_get_due;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_due_new (gboolean date_only,
+                                    gboolean allow_no_date_set)
+{
+       ECompEditorPropertyPart *part;
+
+       part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_DUE,
+               "label", C_("ECompEditor", "D_ue date:"),
+               NULL);
+
+       e_comp_editor_property_part_datetime_labeled_setup (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (part),
+               date_only, allow_no_date_set);
+
+       return part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_COMPLETED \
+       (e_comp_editor_property_part_completed_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_COMPLETED(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_COMPLETED, ECompEditorPropertyPartCompleted))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_COMPLETED(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_COMPLETED))
+
+typedef struct _ECompEditorPropertyPartCompleted ECompEditorPropertyPartCompleted;
+typedef struct _ECompEditorPropertyPartCompletedClass ECompEditorPropertyPartCompletedClass;
+
+struct _ECompEditorPropertyPartCompleted {
+       ECompEditorPropertyPartDatetimeLabeled parent;
+};
+
+struct _ECompEditorPropertyPartCompletedClass {
+       ECompEditorPropertyPartDatetimeLabeledClass parent_class;
+};
+
+GType e_comp_editor_property_part_completed_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartCompleted, e_comp_editor_property_part_completed, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED)
+
+static void
+e_comp_editor_property_part_completed_init (ECompEditorPropertyPartCompleted *part_completed)
+{
+}
+
+static void
+e_comp_editor_property_part_completed_class_init (ECompEditorPropertyPartCompletedClass *klass)
+{
+       ECompEditorPropertyPartDatetimeClass *part_datetime_class;
+
+       part_datetime_class = E_COMP_EDITOR_PROPERTY_PART_DATETIME_CLASS (klass);
+       part_datetime_class->ical_prop_kind = ICAL_COMPLETED_PROPERTY;
+       part_datetime_class->ical_new_func = icalproperty_new_completed;
+       part_datetime_class->ical_set_func = icalproperty_set_completed;
+       part_datetime_class->ical_get_func = icalproperty_get_completed;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_completed_new (gboolean date_only,
+                                          gboolean allow_no_date_set)
+{
+       ECompEditorPropertyPart *part;
+
+       part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_COMPLETED,
+               "label", C_("ECompEditor", "Date _completed:"),
+               NULL);
+
+       e_comp_editor_property_part_datetime_labeled_setup (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME_LABELED (part),
+               date_only, allow_no_date_set);
+
+       return part;
+}
+
+/* ************************************************************************* */
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_classification_new (void)
+{
+       ECompEditorPropertyPartPickerMap map[] = {
+               { ICAL_CLASS_PUBLIC,       NC_("ECompEditor", "Public"),       FALSE, NULL },
+               { ICAL_CLASS_PRIVATE,      NC_("ECompEditor", "Private"),      FALSE, NULL },
+               { ICAL_CLASS_CONFIDENTIAL, NC_("ECompEditor", "Confidential"), FALSE, NULL }
+       };
+       gint ii, n_elems = G_N_ELEMENTS (map);
+
+       for (ii = 0; ii < n_elems; ii++) {
+               map[ii].description = g_dpgettext2 (GETTEXT_PACKAGE, "ECompEditor", map[ii].description);
+       }
+
+       return e_comp_editor_property_part_picker_with_map_new (map, n_elems,
+               C_("ECompEditor", "C_lassification:"),
+               ICAL_CLASS_PROPERTY,
+               (ECompEditorPropertyPartPickerMapICalNewFunc) icalproperty_new_class,
+               (ECompEditorPropertyPartPickerMapICalSetFunc) icalproperty_set_class,
+               (ECompEditorPropertyPartPickerMapICalGetFunc) icalproperty_get_class);
+}
+
+/* ************************************************************************* */
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_status_new (void)
+{
+       ECompEditorPropertyPartPickerMap map[] = {
+               { ICAL_STATUS_NONE,      NC_("ECompEditor", "Not Started"), TRUE,  NULL },
+               { ICAL_STATUS_INPROCESS, NC_("ECompEditor", "In Progress"), FALSE, NULL },
+               { ICAL_STATUS_COMPLETED, NC_("ECompEditor", "Completed"),   FALSE, NULL },
+               { ICAL_STATUS_CANCELLED, NC_("ECompEditor", "Canceled"),    FALSE, NULL }
+       };
+       gint ii, n_elems = G_N_ELEMENTS (map);
+
+       for (ii = 0; ii < n_elems; ii++) {
+               map[ii].description = g_dpgettext2 (GETTEXT_PACKAGE, "ECompEditor", map[ii].description);
+       }
+
+       return e_comp_editor_property_part_picker_with_map_new (map, n_elems,
+               C_("ECompEditor", "_Status:"),
+               ICAL_STATUS_PROPERTY,
+               (ECompEditorPropertyPartPickerMapICalNewFunc) icalproperty_new_status,
+               (ECompEditorPropertyPartPickerMapICalSetFunc) icalproperty_set_status,
+               (ECompEditorPropertyPartPickerMapICalGetFunc) icalproperty_get_status);
+}
+
+/* ************************************************************************* */
+
+static gboolean
+ecepp_priority_matches (gint map_value,
+                       gint component_value)
+{
+       if (map_value == component_value)
+               return TRUE;
+
+       if (component_value == 0)
+               return map_value == 0;
+       else if (component_value <= 4)
+               return map_value == 3;
+       else if (component_value == 5)
+               return map_value == 5;
+       else
+               return map_value == 7;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_priority_new (void)
+{
+       ECompEditorPropertyPartPickerMap map[] = {
+               { 0, NC_("ECompEditor", "Undefined"), TRUE,  ecepp_priority_matches },
+               { 3, NC_("ECompEditor", "High"),      FALSE, ecepp_priority_matches },
+               { 5, NC_("ECompEditor", "Normal"),    FALSE, ecepp_priority_matches },
+               { 7, NC_("ECompEditor", "Low"),       FALSE, ecepp_priority_matches }
+       };
+       gint ii, n_elems = G_N_ELEMENTS (map);
+
+       for (ii = 0; ii < n_elems; ii++) {
+               map[ii].description = g_dpgettext2 (GETTEXT_PACKAGE, "ECompEditor", map[ii].description);
+       }
+
+       return e_comp_editor_property_part_picker_with_map_new (map, n_elems,
+               C_("ECompEditor", "Priorit_y:"),
+               ICAL_PRIORITY_PROPERTY,
+               icalproperty_new_priority,
+               icalproperty_set_priority,
+               icalproperty_get_priority);
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE \
+       (e_comp_editor_property_part_percentcomplete_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE, ECompEditorPropertyPartPercentcomplete))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE))
+
+typedef struct _ECompEditorPropertyPartPercentcomplete ECompEditorPropertyPartPercentcomplete;
+typedef struct _ECompEditorPropertyPartPercentcompleteClass ECompEditorPropertyPartPercentcompleteClass;
+
+struct _ECompEditorPropertyPartPercentcomplete {
+       ECompEditorPropertyPartSpin parent;
+};
+
+struct _ECompEditorPropertyPartPercentcompleteClass {
+       ECompEditorPropertyPartSpinClass parent_class;
+};
+
+GType e_comp_editor_property_part_percentcomplete_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartPercentcomplete, e_comp_editor_property_part_percentcomplete, 
E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN)
+
+static void
+ecepp_percentcomplete_create_widgets (ECompEditorPropertyPart *property_part,
+                                     GtkWidget **out_label_widget,
+                                     GtkWidget **out_edit_widget)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS 
(e_comp_editor_property_part_percentcomplete_parent_class);
+       g_return_if_fail (part_class != NULL);
+       g_return_if_fail (part_class->create_widgets != NULL);
+
+       *out_label_widget = NULL;
+
+       part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
+       g_return_if_fail (*out_label_widget == NULL);
+       g_return_if_fail (*out_edit_widget != NULL);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "Percent complete:"));
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+}
+
+static void
+e_comp_editor_property_part_percentcomplete_init (ECompEditorPropertyPartPercentcomplete 
*part_percentcomplete)
+{
+}
+
+static void
+e_comp_editor_property_part_percentcomplete_class_init (ECompEditorPropertyPartPercentcompleteClass *klass)
+{
+       ECompEditorPropertyPartSpinClass *part_spin_class;
+       ECompEditorPropertyPartClass *part_class;
+
+       part_spin_class = E_COMP_EDITOR_PROPERTY_PART_SPIN_CLASS (klass);
+       part_spin_class->ical_prop_kind = ICAL_PERCENTCOMPLETE_PROPERTY;
+       part_spin_class->ical_new_func = icalproperty_new_percentcomplete;
+       part_spin_class->ical_set_func = icalproperty_set_percentcomplete;
+       part_spin_class->ical_get_func = icalproperty_get_percentcomplete;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_percentcomplete_create_widgets;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_percentcomplete_new (void)
+{
+       ECompEditorPropertyPart *part;
+
+       part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_PERCENTCOMPLETE, NULL);
+
+       e_comp_editor_property_part_spin_set_range (E_COMP_EDITOR_PROPERTY_PART_SPIN (part), 0, 100);
+
+       return part;
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_TIMEZONE \
+       (e_comp_editor_property_part_timezone_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_TIMEZONE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_TIMEZONE, ECompEditorPropertyPartTimezone))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_TIMEZONE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_TIMEZONE))
+
+typedef struct _ECompEditorPropertyPartTimezone ECompEditorPropertyPartTimezone;
+typedef struct _ECompEditorPropertyPartTimezoneClass ECompEditorPropertyPartTimezoneClass;
+
+struct _ECompEditorPropertyPartTimezone {
+       ECompEditorPropertyPart parent;
+};
+
+struct _ECompEditorPropertyPartTimezoneClass {
+       ECompEditorPropertyPartClass parent_class;
+};
+
+GType e_comp_editor_property_part_timezone_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartTimezone, e_comp_editor_property_part_timezone, 
E_TYPE_COMP_EDITOR_PROPERTY_PART)
+
+static void
+ecepp_timezone_create_widgets (ECompEditorPropertyPart *property_part,
+                              GtkWidget **out_label_widget,
+                              GtkWidget **out_edit_widget)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_TIMEZONE (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       *out_label_widget = gtk_label_new_with_mnemonic (C_("ECompEditor", "Time _zone:"));
+
+       g_object_set (G_OBJECT (*out_label_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_END,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_label_widget);
+
+       *out_edit_widget = e_timezone_entry_new ();
+       e_timezone_entry_set_timezone (E_TIMEZONE_ENTRY (*out_edit_widget), calendar_config_get_icaltimezone 
());
+
+       gtk_widget_show (*out_edit_widget);
+
+       gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
+
+       g_signal_connect_swapped (*out_edit_widget, "changed",
+               G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+}
+
+static void
+ecepp_timezone_fill_widget (ECompEditorPropertyPart *property_part,
+                           icalcomponent *component)
+{
+       struct icaltimetype (* get_func) (const icalproperty *prop);
+       icalproperty *prop;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_TIMEZONE (property_part));
+
+       get_func = icalproperty_get_dtstart;
+       prop = icalcomponent_get_first_property (component, ICAL_DTSTART_PROPERTY);
+
+       if (!prop) {
+               get_func = icalproperty_get_dtend;
+               prop = icalcomponent_get_first_property (component, ICAL_DTEND_PROPERTY);
+       }
+
+       if (!prop) {
+               get_func = icalproperty_get_due;
+               prop = icalcomponent_get_first_property (component, ICAL_DUE_PROPERTY);
+       }
+
+       if (prop) {
+               struct icaltimetype itt;
+
+               itt = get_func (prop);
+               if (itt.zone) {
+                       GtkWidget *edit_widget;
+
+                       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+                       g_return_if_fail (E_IS_TIMEZONE_ENTRY (edit_widget));
+
+                       e_timezone_entry_set_timezone (E_TIMEZONE_ENTRY (edit_widget), (icaltimezone *) 
itt.zone);
+               }
+       }
+}
+
+static void
+ecepp_timezone_fill_component (ECompEditorPropertyPart *property_part,
+                              icalcomponent *component)
+{
+       /* Nothing to do here, this is sort-of virtual property part */
+}
+
+static void
+e_comp_editor_property_part_timezone_init (ECompEditorPropertyPartTimezone *part_timezone)
+{
+}
+
+static void
+e_comp_editor_property_part_timezone_class_init (ECompEditorPropertyPartTimezoneClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_timezone_create_widgets;
+       part_class->fill_widget = ecepp_timezone_fill_widget;
+       part_class->fill_component = ecepp_timezone_fill_component;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_timezone_new (void)
+{
+       return g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_TIMEZONE, NULL);
+}
+
+/* ************************************************************************* */
+
+#define E_TYPE_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY \
+       (e_comp_editor_property_part_transparency_get_type ())
+#define E_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY, ECompEditorPropertyPartTransparency))
+#define E_IS_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY))
+
+typedef struct _ECompEditorPropertyPartTransparency ECompEditorPropertyPartTransparency;
+typedef struct _ECompEditorPropertyPartTransparencyClass ECompEditorPropertyPartTransparencyClass;
+
+struct _ECompEditorPropertyPartTransparency {
+       ECompEditorPropertyPart parent;
+};
+
+struct _ECompEditorPropertyPartTransparencyClass {
+       ECompEditorPropertyPartClass parent_class;
+};
+
+GType e_comp_editor_property_part_transparency_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ECompEditorPropertyPartTransparency, e_comp_editor_property_part_transparency, 
E_TYPE_COMP_EDITOR_PROPERTY_PART)
+
+static void
+ecepp_transparency_create_widgets (ECompEditorPropertyPart *property_part,
+                                  GtkWidget **out_label_widget,
+                                  GtkWidget **out_edit_widget)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY (property_part));
+       g_return_if_fail (out_label_widget != NULL);
+       g_return_if_fail (out_edit_widget != NULL);
+
+       *out_label_widget = NULL;
+
+       *out_edit_widget = gtk_check_button_new_with_mnemonic (C_("ECompEditor", "Show time as _busy"));
+
+       g_object_set (G_OBJECT (*out_edit_widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_CENTER,
+               NULL);
+
+       gtk_widget_show (*out_edit_widget);
+
+       g_signal_connect_swapped (*out_edit_widget, "toggled",
+               G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
+}
+
+static void
+ecepp_transparency_fill_widget (ECompEditorPropertyPart *property_part,
+                               icalcomponent *component)
+{
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY (property_part));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_CHECK_BUTTON (edit_widget));
+
+       prop = icalcomponent_get_first_property (component, ICAL_TRANSP_PROPERTY);
+       if (prop) {
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (edit_widget),
+                       icalproperty_get_transp (prop) == ICAL_TRANSP_OPAQUE);
+       } else {
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (edit_widget), TRUE);
+       }
+}
+
+static void
+ecepp_transparency_fill_component (ECompEditorPropertyPart *property_part,
+                                  icalcomponent *component)
+{
+       GtkWidget *edit_widget;
+       icalproperty *prop;
+       icalproperty_transp value;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY (property_part));
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
+       g_return_if_fail (GTK_IS_CHECK_BUTTON (edit_widget));
+
+       value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (edit_widget)) ? ICAL_TRANSP_OPAQUE : 
ICAL_TRANSP_TRANSPARENT;
+
+       prop = icalcomponent_get_first_property (component, ICAL_TRANSP_PROPERTY);
+       if (prop) {
+               icalproperty_set_transp (prop, value);
+       } else {
+               prop = icalproperty_new_transp (value);
+               icalcomponent_add_property (component, prop);
+       }
+}
+
+static void
+e_comp_editor_property_part_transparency_init (ECompEditorPropertyPartTransparency *part_transparency)
+{
+}
+
+static void
+e_comp_editor_property_part_transparency_class_init (ECompEditorPropertyPartTransparencyClass *klass)
+{
+       ECompEditorPropertyPartClass *part_class;
+
+       part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
+       part_class->create_widgets = ecepp_transparency_create_widgets;
+       part_class->fill_widget = ecepp_transparency_fill_widget;
+       part_class->fill_component = ecepp_transparency_fill_component;
+}
+
+ECompEditorPropertyPart *
+e_comp_editor_property_part_transparency_new (void)
+{
+       return g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_TRANSPARENCY, NULL);
+}
+
+/* ************************************************************************* */
diff --git a/calendar/gui/e-comp-editor-property-parts.h b/calendar/gui/e-comp-editor-property-parts.h
new file mode 100644
index 0000000..68a1bbe
--- /dev/null
+++ b/calendar/gui/e-comp-editor-property-parts.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_PROPERTY_PARTS_H
+#define E_COMP_EDITOR_PROPERTY_PARTS_H
+
+#include <libecal/libecal.h>
+#include <e-util/e-util.h>
+#include <calendar/gui/e-comp-editor-property-part.h>
+
+G_BEGIN_DECLS
+
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_summary_new         (EFocusTracker *focus_tracker);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_location_new        (EFocusTracker *focus_tracker);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_categories_new      (EFocusTracker *focus_tracker);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_description_new     (EFocusTracker *focus_tracker);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_url_new             (EFocusTracker *focus_tracker);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_dtstart_new         (const gchar *label,
+                                                                gboolean date_only,
+                                                                gboolean allow_no_date_set);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_dtend_new           (const gchar *label,
+                                                                gboolean date_only,
+                                                                gboolean allow_no_date_set);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_due_new             (gboolean date_only,
+                                                                gboolean allow_no_date_set);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_completed_new       (gboolean date_only,
+                                                                gboolean allow_no_date_set);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_classification_new  (void);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_status_new          (void);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_priority_new        (void);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_percentcomplete_new (void);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_timezone_new        (void);
+ECompEditorPropertyPart *
+               e_comp_editor_property_part_transparency_new    (void);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_PROPERTY_PARTS_H */
diff --git a/calendar/gui/e-comp-editor-task.c b/calendar/gui/e-comp-editor-task.c
new file mode 100644
index 0000000..36c2be0
--- /dev/null
+++ b/calendar/gui/e-comp-editor-task.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <e-util/e-util.h>
+
+#include "comp-util.h"
+#include "e-comp-editor.h"
+#include "e-comp-editor-page.h"
+#include "e-comp-editor-page-attachments.h"
+#include "e-comp-editor-page-general.h"
+#include "e-comp-editor-property-part.h"
+#include "e-comp-editor-property-parts.h"
+
+#include "e-comp-editor-task.h"
+
+struct _ECompEditorTaskPrivate {
+       ECompEditorPage *page_general;
+       ECompEditorPropertyPart *categories;
+       ECompEditorPropertyPart *dtstart;
+       ECompEditorPropertyPart *due_date;
+       ECompEditorPropertyPart *completed_date;
+       ECompEditorPropertyPart *percentcomplete;
+       ECompEditorPropertyPart *status;
+
+       gpointer in_the_past_alert;
+       gpointer insensitive_info_alert;
+};
+
+G_DEFINE_TYPE (ECompEditorTask, e_comp_editor_task, E_TYPE_COMP_EDITOR)
+
+static void
+ece_task_check_dates_in_the_past (ECompEditorTask *task_editor)
+{
+       guint32 flags;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       flags = e_comp_editor_get_flags (E_COMP_EDITOR (task_editor));
+
+       if ((flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0) {
+               GString *message = NULL;
+               struct icaltimetype dtstart_itt, due_date_itt;
+
+               dtstart_itt = e_comp_editor_property_part_datetime_get_value (
+                       E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->dtstart));
+               due_date_itt = e_comp_editor_property_part_datetime_get_value (
+                       E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->due_date));
+
+               if (cal_comp_util_compare_time_with_today (dtstart_itt) < 0)
+                       message = g_string_new (_("Task's start date is in the past"));
+
+               if (cal_comp_util_compare_time_with_today (due_date_itt) < 0) {
+                       if (message)
+                               g_string_append (message, "\n");
+                       else
+                               message = g_string_new ("");
+
+                       g_string_append (message, _("Task's due date is in the past"));
+               }
+
+               if (message) {
+                       EAlert *alert;
+
+                       alert = e_comp_editor_add_warning (E_COMP_EDITOR (task_editor), message->str, NULL);
+
+                       if (task_editor->priv->in_the_past_alert)
+                               e_alert_response (task_editor->priv->in_the_past_alert, GTK_RESPONSE_OK);
+
+                       task_editor->priv->in_the_past_alert = alert;
+
+                       if (alert)
+                               g_object_add_weak_pointer (G_OBJECT (alert), 
&task_editor->priv->in_the_past_alert);
+
+                       g_string_free (message, TRUE);
+                       g_clear_object (&alert);
+               } else if (task_editor->priv->in_the_past_alert) {
+                       e_alert_response (task_editor->priv->in_the_past_alert, GTK_RESPONSE_OK);
+               }
+       }
+}
+
+static void
+ece_task_dtstart_changed_cb (EDateEdit *date_edit,
+                            ECompEditorTask *task_editor)
+{
+       ECompEditor *comp_editor;
+
+       g_return_if_fail (E_IS_DATE_EDIT (date_edit));
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       comp_editor = E_COMP_EDITOR (task_editor);
+
+       if (e_comp_editor_get_updating (comp_editor))
+               return;
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+
+       e_comp_editor_ensure_start_before_end (E_COMP_EDITOR (task_editor),
+               task_editor->priv->dtstart, task_editor->priv->due_date,
+               TRUE);
+
+       e_comp_editor_set_updating (comp_editor, FALSE);
+
+       ece_task_check_dates_in_the_past (task_editor);
+}
+
+static void
+ece_task_due_date_changed_cb (EDateEdit *date_edit,
+                             ECompEditorTask *task_editor)
+{
+       ECompEditor *comp_editor;
+
+       g_return_if_fail (E_IS_DATE_EDIT (date_edit));
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       comp_editor = E_COMP_EDITOR (task_editor);
+
+       if (e_comp_editor_get_updating (comp_editor))
+               return;
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+
+       e_comp_editor_ensure_start_before_end (E_COMP_EDITOR (task_editor),
+               task_editor->priv->dtstart, task_editor->priv->due_date,
+               FALSE);
+
+       e_comp_editor_set_updating (comp_editor, FALSE);
+
+       ece_task_check_dates_in_the_past (task_editor);
+}
+
+static void
+ece_task_completed_date_changed_cb (EDateEdit *date_edit,
+                                   ECompEditorTask *task_editor)
+{
+       GtkSpinButton *percent_spin;
+       ECompEditor *comp_editor;
+       struct icaltimetype itt;
+       gint status;
+
+       g_return_if_fail (E_IS_DATE_EDIT (date_edit));
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       comp_editor = E_COMP_EDITOR (task_editor);
+
+       if (e_comp_editor_get_updating (comp_editor))
+               return;
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+
+       status = e_comp_editor_property_part_picker_with_map_get_selected (
+               E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (task_editor->priv->status));
+       itt = e_comp_editor_property_part_datetime_get_value (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->completed_date));
+       percent_spin = GTK_SPIN_BUTTON (e_comp_editor_property_part_get_edit_widget 
(task_editor->priv->percentcomplete));
+
+       if (icaltime_is_null_time (itt)) {
+               if (status == ICAL_STATUS_COMPLETED) {
+                       e_comp_editor_property_part_picker_with_map_set_selected (
+                               E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (task_editor->priv->status),
+                               ICAL_STATUS_NONE);
+
+                       gtk_spin_button_set_value (percent_spin, 0);
+               }
+       } else {
+               if (status != ICAL_STATUS_COMPLETED) {
+                       e_comp_editor_property_part_picker_with_map_set_selected (
+                               E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (task_editor->priv->status),
+                               ICAL_STATUS_COMPLETED);
+               }
+
+               gtk_spin_button_set_value (percent_spin, 100);
+       }
+
+       e_comp_editor_set_updating (comp_editor, FALSE);
+}
+
+static void
+ece_task_status_changed_cb (GtkComboBox *combo_box,
+                           ECompEditorTask *task_editor)
+{
+       ECompEditor *comp_editor;
+       GtkSpinButton *percent_spin;
+       EDateEdit *completed_date;
+       gint status;
+
+       g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       comp_editor = E_COMP_EDITOR (task_editor);
+
+       if (e_comp_editor_get_updating (comp_editor))
+               return;
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+
+       percent_spin = GTK_SPIN_BUTTON (e_comp_editor_property_part_get_edit_widget 
(task_editor->priv->percentcomplete));
+       completed_date = E_DATE_EDIT (e_comp_editor_property_part_get_edit_widget 
(task_editor->priv->completed_date));
+       status = e_comp_editor_property_part_picker_with_map_get_selected (
+               E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (task_editor->priv->status));
+
+       if (status == ICAL_STATUS_NONE) {
+               gtk_spin_button_set_value (percent_spin, 0);
+               e_date_edit_set_time (completed_date, (time_t) -1);
+       } else if (status == ICAL_STATUS_INPROCESS) {
+               gint percent_complete = gtk_spin_button_get_value_as_int (percent_spin);
+
+               if (percent_complete <= 0 || percent_complete >= 100)
+                       gtk_spin_button_set_value (percent_spin, 50);
+
+               e_date_edit_set_time (completed_date, (time_t) -1);
+       } else if (status == ICAL_STATUS_COMPLETED) {
+               gtk_spin_button_set_value (percent_spin, 100);
+               e_date_edit_set_time (completed_date, time (NULL));
+       }
+
+       e_comp_editor_set_updating (comp_editor, FALSE);
+}
+
+static void
+ece_task_percentcomplete_value_changed_cb (GtkSpinButton *spin_button,
+                                          ECompEditorTask *task_editor)
+{
+       ECompEditor *comp_editor;
+       GtkSpinButton *percent_spin;
+       EDateEdit *completed_date;
+       gint status, percent;
+       time_t ctime;
+
+       g_return_if_fail (GTK_IS_SPIN_BUTTON (spin_button));
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       comp_editor = E_COMP_EDITOR (task_editor);
+
+       if (e_comp_editor_get_updating (comp_editor))
+               return;
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+
+       percent_spin = GTK_SPIN_BUTTON (e_comp_editor_property_part_get_edit_widget 
(task_editor->priv->percentcomplete));
+       completed_date = E_DATE_EDIT (e_comp_editor_property_part_get_edit_widget 
(task_editor->priv->completed_date));
+
+       percent = gtk_spin_button_get_value_as_int (percent_spin);
+       if (percent == 100) {
+               ctime = time (NULL);
+               status = ICAL_STATUS_COMPLETED;
+       } else {
+               ctime = (time_t) -1;
+
+               if (percent == 0)
+                       status = ICAL_STATUS_NONE;
+               else
+                       status = ICAL_STATUS_INPROCESS;
+       }
+
+       e_comp_editor_property_part_picker_with_map_set_selected (
+               E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (task_editor->priv->status), status);
+       e_date_edit_set_time (completed_date, ctime);
+
+       e_comp_editor_set_updating (comp_editor, FALSE);
+}
+
+static void
+ece_task_sensitize_widgets (ECompEditor *comp_editor,
+                           gboolean force_insensitive)
+{
+       ECompEditorTask *task_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (comp_editor));
+
+       E_COMP_EDITOR_CLASS (e_comp_editor_task_parent_class)->sensitize_widgets (comp_editor, 
force_insensitive);
+
+       task_editor = E_COMP_EDITOR_TASK (comp_editor);
+
+       if (force_insensitive) {
+               ECalClient *client;
+               const gchar *message = NULL;
+
+               client = e_comp_editor_get_target_client (comp_editor);
+               if (!client)
+                       message = _("Task cannot be edited, because the selected task list could not be 
opened");
+               else if (e_client_is_readonly (E_CLIENT (client)))
+                       message = _("Task cannot be edited, because the selected task list is read only");
+
+               if (message) {
+                       EAlert *alert;
+
+                       alert = e_comp_editor_add_information (comp_editor, message, NULL);
+
+                       if (task_editor->priv->insensitive_info_alert)
+                               e_alert_response (task_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
+
+                       task_editor->priv->insensitive_info_alert = alert;
+
+                       if (alert)
+                               g_object_add_weak_pointer (G_OBJECT (alert), 
&task_editor->priv->insensitive_info_alert);
+
+                       g_clear_object (&alert);
+               } else  if (task_editor->priv->insensitive_info_alert) {
+                       e_alert_response (task_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
+               }
+
+       } else if (task_editor->priv->insensitive_info_alert) {
+               e_alert_response (task_editor->priv->insensitive_info_alert, GTK_RESPONSE_OK);
+       }
+
+       ece_task_check_dates_in_the_past (task_editor);
+}
+
+static gboolean
+ece_task_fill_component (ECompEditor *comp_editor,
+                        icalcomponent *component)
+{
+       ECompEditorTask *task_editor;
+       struct icaltimetype itt;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR_TASK (comp_editor), FALSE);
+
+       task_editor = E_COMP_EDITOR_TASK (comp_editor);
+
+       if (!e_comp_editor_property_part_datetime_check_validity (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->dtstart), NULL, NULL)) {
+
+               e_comp_editor_set_validation_error (comp_editor,
+                       task_editor->priv->page_general,
+                       e_comp_editor_property_part_get_edit_widget (task_editor->priv->dtstart),
+                       _("Start date is not a valid date"));
+
+               return FALSE;
+       }
+
+       if (!e_comp_editor_property_part_datetime_check_validity (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->due_date), NULL, NULL)) {
+
+               e_comp_editor_set_validation_error (comp_editor,
+                       task_editor->priv->page_general,
+                       e_comp_editor_property_part_get_edit_widget (task_editor->priv->due_date),
+                       _("Due date is not a valid date"));
+
+               return FALSE;
+       }
+
+       if (!e_comp_editor_property_part_datetime_check_validity (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->completed_date), NULL, NULL)) {
+
+               e_comp_editor_set_validation_error (comp_editor,
+                       task_editor->priv->page_general,
+                       e_comp_editor_property_part_get_edit_widget (task_editor->priv->completed_date),
+                       _("Completed date is not a valid date"));
+
+               return FALSE;
+       }
+
+       itt = e_comp_editor_property_part_datetime_get_value (
+               E_COMP_EDITOR_PROPERTY_PART_DATETIME (task_editor->priv->completed_date));
+       if (cal_comp_util_compare_time_with_today (itt) > 0) {
+               e_comp_editor_set_validation_error (comp_editor,
+                       task_editor->priv->page_general,
+                       e_comp_editor_property_part_get_edit_widget (task_editor->priv->completed_date),
+                       _("Completed date cannot be in the future"));
+
+               return FALSE;
+       }
+
+       return E_COMP_EDITOR_CLASS (e_comp_editor_task_parent_class)->fill_component (comp_editor, component);
+}
+
+static void
+ece_task_setup_ui (ECompEditorTask *task_editor)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='view-menu'>"
+               "      <placeholder name='parts'>"
+               "        <menuitem action='view-categories'/>"
+               "      </placeholder>"
+               "    </menu>"
+               "  </menubar>"
+               "</ui>";
+
+       const GtkToggleActionEntry view_actions[] = {
+               { "view-categories",
+                 NULL,
+                 N_("_Categories"),
+                 NULL,
+                 N_("Toggles whether to display categories"),
+                 NULL,
+                 FALSE }
+       };
+
+       ECompEditor *comp_editor;
+       GSettings *settings;
+       GtkUIManager *ui_manager;
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_COMP_EDITOR_TASK (task_editor));
+
+       comp_editor = E_COMP_EDITOR (task_editor);
+       settings = e_comp_editor_get_settings (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_toggle_actions (action_group,
+               view_actions, G_N_ELEMENTS (view_actions), task_editor);
+
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
+
+       e_plugin_ui_register_manager (ui_manager, "org.gnome.evolution.task-editor", task_editor);
+       e_plugin_ui_enable_manager (ui_manager, "org.gnome.evolution.task-editor");
+
+       if (error) {
+               g_critical ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+
+       action = e_comp_editor_get_action (comp_editor, "view-categories");
+       e_binding_bind_property (
+               task_editor->priv->categories, "visible",
+               action, "active",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       g_settings_bind (
+               settings, "editor-show-categories",
+               action, "active",
+               G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+e_comp_editor_task_constructed (GObject *object)
+{
+       ECompEditorTask *task_editor;
+       ECompEditor *comp_editor;
+       ECompEditorPage *page;
+       ECompEditorPropertyPart *part, *summary;
+       EFocusTracker *focus_tracker;
+       GtkWidget *edit_widget;
+
+       G_OBJECT_CLASS (e_comp_editor_task_parent_class)->constructed (object);
+
+       task_editor = E_COMP_EDITOR_TASK (object);
+       comp_editor = E_COMP_EDITOR (task_editor);
+       focus_tracker = e_comp_editor_get_focus_tracker (comp_editor);
+
+       page = e_comp_editor_page_general_new (comp_editor,
+               _("_List:"), E_SOURCE_EXTENSION_TASK_LIST,
+               NULL, FALSE, 3);
+
+       part = e_comp_editor_property_part_summary_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 2, 4, 1);
+       summary = part;
+
+       part = e_comp_editor_property_part_dtstart_new (C_("ECompEditor", "Sta_rt date:"), TRUE, TRUE);
+       e_comp_editor_page_add_property_part (page, part, 0, 3, 2, 1);
+       task_editor->priv->dtstart = part;
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       g_signal_connect (edit_widget, "changed", G_CALLBACK (ece_task_dtstart_changed_cb), task_editor);
+
+       part = e_comp_editor_property_part_status_new ();
+       e_comp_editor_page_add_property_part (page, part, 2, 3, 2, 1);
+       task_editor->priv->status = part;
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       g_signal_connect (edit_widget, "changed", G_CALLBACK (ece_task_status_changed_cb), task_editor);
+
+       part = e_comp_editor_property_part_due_new (TRUE, TRUE);
+       e_comp_editor_page_add_property_part (page, part, 0, 4, 2, 1);
+       task_editor->priv->due_date = part;
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       g_signal_connect (edit_widget, "changed", G_CALLBACK (ece_task_due_date_changed_cb), task_editor);
+
+       part = e_comp_editor_property_part_priority_new ();
+       e_comp_editor_page_add_property_part (page, part, 2, 4, 2, 1);
+
+       part = e_comp_editor_property_part_completed_new (TRUE, TRUE);
+       e_comp_editor_page_add_property_part (page, part, 0, 5, 2, 1);
+       task_editor->priv->completed_date = part;
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       g_signal_connect (edit_widget, "changed", G_CALLBACK (ece_task_completed_date_changed_cb), 
task_editor);
+
+       part = e_comp_editor_property_part_percentcomplete_new ();
+       e_comp_editor_page_add_property_part (page, part, 2, 5, 2, 1);
+       task_editor->priv->percentcomplete = part;
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       g_signal_connect (edit_widget, "value-changed", G_CALLBACK 
(ece_task_percentcomplete_value_changed_cb), task_editor);
+
+       part = e_comp_editor_property_part_url_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 6, 2, 1);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (part);
+       gtk_widget_set_hexpand (edit_widget, TRUE);
+
+       part = e_comp_editor_property_part_classification_new ();
+       e_comp_editor_page_add_property_part (page, part, 2, 6, 2, 1);
+
+       part = e_comp_editor_property_part_categories_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 8, 4, 1);
+       task_editor->priv->categories = part;
+
+       part = e_comp_editor_property_part_description_new (focus_tracker);
+       e_comp_editor_page_add_property_part (page, part, 0, 9, 4, 1);
+
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "General"), page);
+       task_editor->priv->page_general = page;
+
+       e_comp_editor_set_time_parts (comp_editor, task_editor->priv->dtstart, task_editor->priv->due_date);
+
+       page = e_comp_editor_page_attachments_new (comp_editor);
+       e_comp_editor_add_page (comp_editor, C_("ECompEditorPage", "Attachments"), page);
+
+       ece_task_setup_ui (task_editor);
+
+       edit_widget = e_comp_editor_property_part_get_edit_widget (summary);
+       e_binding_bind_property (edit_widget, "text", comp_editor, "title-suffix", 0);
+       gtk_widget_grab_focus (edit_widget);
+}
+
+static void
+e_comp_editor_task_init (ECompEditorTask *task_editor)
+{
+       task_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (task_editor, E_TYPE_COMP_EDITOR_TASK, 
ECompEditorTaskPrivate);
+}
+
+static void
+e_comp_editor_task_class_init (ECompEditorTaskClass *klass)
+{
+       GObjectClass *object_class;
+       ECompEditorClass *comp_editor_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorTaskPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_comp_editor_task_constructed;
+
+       comp_editor_class = E_COMP_EDITOR_CLASS (klass);
+       comp_editor_class->help_section = "tasks-usage";
+       comp_editor_class->title_format_with_attendees = _("Assigned Task - %s");
+       comp_editor_class->title_format_without_attendees = _("Task - %s");
+       comp_editor_class->icon_name = "stock_task";
+       comp_editor_class->sensitize_widgets = ece_task_sensitize_widgets;
+       comp_editor_class->fill_component = ece_task_fill_component;
+}
diff --git a/calendar/gui/e-comp-editor-task.h b/calendar/gui/e-comp-editor-task.h
new file mode 100644
index 0000000..229fcb4
--- /dev/null
+++ b/calendar/gui/e-comp-editor-task.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_TASK_H
+#define E_COMP_EDITOR_TASK_H
+
+#include <calendar/gui/e-comp-editor.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR_TASK \
+       (e_comp_editor_task_get_type ())
+#define E_COMP_EDITOR_TASK(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR_TASK, ECompEditorTask))
+#define E_COMP_EDITOR_TASK_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR_TASK, ECompEditorTaskClass))
+#define E_IS_COMP_EDITOR_TASK(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR_TASK))
+#define E_IS_COMP_EDITOR_TASK_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR_TASK))
+#define E_COMP_EDITOR_TASK_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR_TASK, ECompEditorTaskClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECompEditorTask ECompEditorTask;
+typedef struct _ECompEditorTaskClass ECompEditorTaskClass;
+typedef struct _ECompEditorTaskPrivate ECompEditorTaskPrivate;
+
+struct _ECompEditorTask {
+       ECompEditor parent;
+
+       ECompEditorTaskPrivate *priv;
+};
+
+struct _ECompEditorTaskClass {
+       ECompEditorClass parent_class;
+};
+
+GType          e_comp_editor_task_get_type     (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_TASK_H */
diff --git a/calendar/gui/e-comp-editor.c b/calendar/gui/e-comp-editor.c
new file mode 100644
index 0000000..0afc487
--- /dev/null
+++ b/calendar/gui/e-comp-editor.c
@@ -0,0 +1,3267 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+#include <libecal/libecal.h>
+#include <e-util/e-util.h>
+
+#include "calendar-config.h"
+#include "comp-util.h"
+#include "e-cal-dialogs.h"
+#include "itip-utils.h"
+#include "print.h"
+
+#include "e-comp-editor-page-general.h"
+#include "e-comp-editor-page-attachments.h"
+#include "e-comp-editor-event.h"
+#include "e-comp-editor-memo.h"
+#include "e-comp-editor-task.h"
+
+#include "e-comp-editor.h"
+
+struct _ECompEditorPrivate {
+       EAlertBar *alert_bar; /* not referenced */
+       EActivityBar *activity_bar; /* not referenced */
+       GtkNotebook *content; /* not referenced */
+
+       EAlert *validation_alert;
+
+       EShell *shell;
+       GSettings *calendar_settings;
+       ESource *origin_source;
+       icalcomponent *component;
+       guint32 flags;
+
+       EFocusTracker *focus_tracker;
+       GtkUIManager *ui_manager;
+
+       GSList *pages; /* ECompEditorPage * */
+       gulong show_attendees_handler_id;
+
+       ECompEditorPageGeneral *page_general; /* special page, can be added only once; not referenced */
+
+       EActivity *target_client_opening;
+
+       ECalClient *source_client;
+       ECalClient *target_client;
+       gchar *cal_email_address;
+       gchar *alarm_email_address;
+       gboolean changed;
+       guint updating;
+       gchar *title_suffix;
+
+       ECompEditorPropertyPart *dtstart_part;
+       ECompEditorPropertyPart *dtend_part;
+
+       GtkWidget *restore_focus;
+};
+
+enum {
+       PROP_0,
+       PROP_ALARM_EMAIL_ADDRESS,
+       PROP_CAL_EMAIL_ADDRESS,
+       PROP_CHANGED,
+       PROP_COMPONENT,
+       PROP_FLAGS,
+       PROP_ORIGIN_SOURCE,
+       PROP_SHELL,
+       PROP_SOURCE_CLIENT,
+       PROP_TARGET_CLIENT,
+       PROP_TITLE_SUFFIX
+};
+
+enum {
+       TIMES_CHANGED,
+       OBJECT_CREATED,
+       EDITOR_CLOSED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static GSList *opened_editors = NULL;
+
+static void e_comp_editor_alert_sink_iface_init (EAlertSinkInterface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ECompEditor, e_comp_editor, GTK_TYPE_WINDOW,
+       G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, e_comp_editor_alert_sink_iface_init))
+
+static void
+ece_restore_focus (ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (comp_editor->priv->restore_focus) {
+               gtk_widget_grab_focus (comp_editor->priv->restore_focus);
+
+               if (GTK_IS_ENTRY (comp_editor->priv->restore_focus))
+                       gtk_editable_set_position (GTK_EDITABLE (comp_editor->priv->restore_focus), 0);
+
+               comp_editor->priv->restore_focus = NULL;
+       }
+}
+
+static void
+e_comp_editor_enable (ECompEditor *comp_editor,
+                     gboolean enable)
+{
+       GtkActionGroup *group;
+       GtkWidget *current_focus;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       current_focus = gtk_window_get_focus (GTK_WINDOW (comp_editor));
+
+       gtk_widget_set_sensitive (GTK_WIDGET (comp_editor->priv->content), enable);
+
+       group = e_comp_editor_get_action_group (comp_editor, "individual");
+       gtk_action_group_set_sensitive (group, enable);
+
+       group = e_comp_editor_get_action_group (comp_editor, "core");
+       gtk_action_group_set_sensitive (group, enable);
+
+       group = e_comp_editor_get_action_group (comp_editor, "editable");
+       gtk_action_group_set_sensitive (group, enable);
+
+       if (enable) {
+               e_comp_editor_sensitize_widgets (comp_editor);
+               ece_restore_focus (comp_editor);
+       } else {
+               comp_editor->priv->restore_focus = current_focus;
+       }
+}
+
+static void
+ece_set_attendees_for_delegation (ECalComponent *comp,
+                                 const gchar *address)
+{
+       icalproperty *prop;
+       icalparameter *param;
+       icalcomponent *icalcomp;
+       gboolean again;
+
+       icalcomp = e_cal_component_get_icalcomponent (comp);
+
+       for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+            prop;
+            prop = again ? icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY) :
+            icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
+               const gchar *attendee = icalproperty_get_attendee (prop);
+               const gchar *delfrom = NULL;
+
+               again = FALSE;
+               param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER);
+               if (param)
+                       delfrom = icalparameter_get_delegatedfrom (param);
+               if (!(g_str_equal (itip_strip_mailto (attendee), address) ||
+                    ((delfrom && *delfrom) && g_str_equal (itip_strip_mailto (delfrom), address)))) {
+                       icalcomponent_remove_property (icalcomp, prop);
+                       icalproperty_free (prop);
+                       again = TRUE;
+               }
+
+       }
+}
+
+/* Utility function to get the mime-attachment list from the attachment
+ * bar for sending the comp via itip. The list and its contents must
+ * be freed by the caller.
+ */
+static GSList *
+ece_get_mime_attach_list (ECompEditor *comp_editor)
+{
+       ECompEditorPage *page_attachments;
+       EAttachmentStore *store;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       struct CalMimeAttach *cal_mime_attach;
+       GSList *attach_list = NULL;
+       gboolean valid;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       page_attachments = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS);
+       if (!page_attachments)
+               return NULL;
+
+       store = e_comp_editor_page_attachments_get_store (E_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
+       if (!store)
+               return NULL;
+
+       model = GTK_TREE_MODEL (store);
+       valid = gtk_tree_model_get_iter_first (model, &iter);
+
+       while (valid) {
+               EAttachment *attachment;
+               CamelDataWrapper *wrapper;
+               CamelMimePart *mime_part;
+               CamelStream *stream;
+               GByteArray *byte_array;
+               guchar *buffer = NULL;
+               const gchar *description;
+               const gchar *disposition;
+               gint column_id;
+
+               column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT;
+               gtk_tree_model_get (model, &iter, column_id, &attachment, -1);
+               mime_part = e_attachment_ref_mime_part (attachment);
+               g_object_unref (attachment);
+
+               valid = gtk_tree_model_iter_next (model, &iter);
+
+               if (mime_part == NULL)
+                       continue;
+
+               cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach));
+               wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+
+               byte_array = g_byte_array_new ();
+               stream = camel_stream_mem_new_with_byte_array (byte_array);
+
+               camel_data_wrapper_decode_to_stream_sync (
+                       wrapper, stream, NULL, NULL);
+               buffer = g_memdup (byte_array->data, byte_array->len);
+
+               camel_mime_part_set_content_id (mime_part, NULL);
+
+               cal_mime_attach->encoded_data = (gchar *) buffer;
+               cal_mime_attach->length = byte_array->len;
+               cal_mime_attach->filename =
+                       g_strdup (camel_mime_part_get_filename (mime_part));
+               description = camel_mime_part_get_description (mime_part);
+               if (description == NULL || *description == '\0')
+                       description = _("attachment");
+               cal_mime_attach->description = g_strdup (description);
+               cal_mime_attach->content_type = g_strdup (
+                       camel_data_wrapper_get_mime_type (wrapper));
+               cal_mime_attach->content_id = g_strdup (
+                       camel_mime_part_get_content_id (mime_part));
+
+               disposition = camel_mime_part_get_disposition (mime_part);
+               cal_mime_attach->disposition =
+                       (disposition != NULL) &&
+                       (g_ascii_strcasecmp (disposition, "inline") == 0);
+
+               attach_list = g_slist_append (attach_list, cal_mime_attach);
+
+               g_object_unref (mime_part);
+               g_object_unref (stream);
+
+       }
+
+       return attach_list;
+}
+
+static void
+e_comp_editor_set_component (ECompEditor *comp_editor,
+                            const icalcomponent *component)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (component != NULL);
+
+       if (comp_editor->priv->component)
+               icalcomponent_free (comp_editor->priv->component);
+       comp_editor->priv->component = icalcomponent_new_clone ((icalcomponent *) component);
+
+       g_warn_if_fail (comp_editor->priv->component != NULL);
+}
+
+static void
+e_comp_editor_set_flags (ECompEditor *comp_editor,
+                        guint32 flags)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       comp_editor->priv->flags = flags;
+}
+
+typedef struct _SaveData {
+       ECompEditor *comp_editor;
+       ECalClient *source_client;
+       ECalClient *target_client;
+       icalcomponent *component;
+       gboolean with_send;
+       gboolean close_after_save;
+       ECalObjModType recur_mod;
+       gboolean success;
+       GError *error;
+       gchar *alert_ident;
+       gchar *alert_arg_0;
+
+       gboolean object_created;
+       ECalComponentItipMethod first_send;
+       ECalComponentItipMethod second_send;
+       ECalComponent *send_comp;
+       EActivity *send_activity;
+       gboolean strip_alarms;
+       gboolean only_new_attendees;
+       GSList *mime_attach_list;
+} SaveData;
+
+static void
+save_data_free (SaveData *sd)
+{
+       if (sd) {
+               if (sd->success) {
+                       if (sd->close_after_save) {
+                               g_signal_emit (sd->comp_editor, signals[EDITOR_CLOSED], 0, TRUE, NULL);
+                               gtk_widget_destroy (GTK_WIDGET (sd->comp_editor));
+                       } else {
+                               e_comp_editor_set_component (sd->comp_editor, sd->component);
+
+                               e_comp_editor_enable (sd->comp_editor, TRUE);
+                               e_comp_editor_fill_widgets (sd->comp_editor, sd->component);
+
+                               g_clear_object (&sd->comp_editor->priv->source_client);
+                               sd->comp_editor->priv->source_client = g_object_ref (sd->target_client);
+
+                               sd->comp_editor->priv->flags = sd->comp_editor->priv->flags & 
(~E_COMP_EDITOR_FLAG_IS_NEW);
+
+                               e_comp_editor_set_changed (sd->comp_editor, FALSE);
+                       }
+               } else if (sd->alert_ident) {
+                       e_alert_submit (
+                               E_ALERT_SINK (sd->comp_editor), sd->alert_ident, sd->alert_arg_0,
+                               sd->error ? sd->error->message : _("Unknown error"), NULL);
+               }
+
+               if (sd->send_activity && e_activity_get_state (sd->send_activity) != E_ACTIVITY_CANCELLED)
+                       e_activity_set_state (sd->send_activity, E_ACTIVITY_COMPLETED);
+
+               g_clear_object (&sd->comp_editor);
+               g_clear_object (&sd->source_client);
+               g_clear_object (&sd->target_client);
+               g_clear_object (&sd->send_comp);
+               g_clear_object (&sd->send_activity);
+               g_clear_error (&sd->error);
+               if (sd->component)
+                       icalcomponent_free (sd->component);
+               g_slist_free_full (sd->mime_attach_list, itip_cal_mime_attach_free);
+               g_free (sd->alert_ident);
+               g_free (sd->alert_arg_0);
+               g_free (sd);
+       }
+}
+
+static gboolean
+ece_send_process_method (SaveData *sd,
+                        ECalComponentItipMethod send_method,
+                        ECalComponent *send_comp,
+                        ESourceRegistry *registry,
+                        GCancellable *cancellable,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
+{
+       GSList *mime_attach_list = NULL;
+
+       g_return_val_if_fail (sd != NULL, FALSE);
+       g_return_val_if_fail (E_IS_CAL_COMPONENT (send_comp), FALSE);
+       g_return_val_if_fail (send_method != E_CAL_COMPONENT_METHOD_NONE, FALSE);
+
+       if (e_cal_component_has_attachments (send_comp) &&
+           e_client_check_capability (E_CLIENT (sd->target_client), CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
+               /* Clone the component with attachments set to CID:...  */
+               GSList *attach_list = NULL;
+               GSList *attach;
+
+               /* mime_attach_list is freed by itip_send_comp_sync */
+               mime_attach_list = sd->mime_attach_list;
+               sd->mime_attach_list = NULL;
+
+               for (attach = mime_attach_list; attach; attach = attach->next) {
+                       struct CalMimeAttach *cma = (struct CalMimeAttach *) attach->data;
+
+                       attach_list = g_slist_append (
+                               attach_list, g_strconcat (
+                               "cid:", cma->content_id, NULL));
+               }
+
+               if (attach_list) {
+                       e_cal_component_set_attachment_list (send_comp, attach_list);
+
+                       g_slist_free_full (attach_list, g_free);
+               }
+       }
+
+       itip_send_component (
+               registry, send_method, send_comp, sd->target_client,
+               NULL, mime_attach_list, NULL, sd->strip_alarms,
+               sd->only_new_attendees, FALSE,
+               cancellable, callback, user_data);
+
+       return TRUE;
+}
+
+static void
+ecep_second_send_processed_cb (GObject *source_object,
+                              GAsyncResult *result,
+                              gpointer user_data)
+{
+       SaveData *sd = user_data;
+
+       g_return_if_fail (sd != NULL);
+
+       sd->success = itip_send_component_finish (result, &sd->error);
+
+       save_data_free (sd);
+}
+
+static void
+ecep_first_send_processed_cb (GObject *source_object,
+                             GAsyncResult *result,
+                             gpointer user_data)
+{
+       SaveData *sd = user_data;
+
+       g_return_if_fail (sd != NULL);
+
+       sd->success = itip_send_component_finish (result, &sd->error);
+       if (!sd->success || sd->second_send == E_CAL_COMPONENT_METHOD_NONE) {
+               save_data_free (sd);
+       } else {
+               sd->success = ece_send_process_method (sd, sd->second_send, sd->send_comp,
+                       e_shell_get_registry (sd->comp_editor->priv->shell),
+                       e_activity_get_cancellable (sd->send_activity),
+                       ecep_second_send_processed_cb, sd);
+               if (!sd->success)
+                       save_data_free (sd);
+       }
+}
+
+static void
+ece_prepare_send_component_done (gpointer ptr)
+{
+       SaveData *sd = ptr;
+
+       g_return_if_fail (sd != NULL);
+       g_return_if_fail (E_IS_COMP_EDITOR (sd->comp_editor));
+       g_return_if_fail (sd->send_activity != NULL);
+
+       sd->success = ece_send_process_method (sd, sd->first_send, sd->send_comp,
+               e_shell_get_registry (sd->comp_editor->priv->shell),
+               e_activity_get_cancellable (sd->send_activity),
+               ecep_first_send_processed_cb, sd);
+       if (!sd->success)
+               save_data_free (sd);
+}
+
+static void
+ece_prepare_send_component_thread (EAlertSinkThreadJobData *job_data,
+                                  gpointer user_data,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+       SaveData *sd = user_data;
+       const gchar *alert_ident;
+       ECalComponent *send_comp = NULL;
+       guint32 flags;
+       ESourceRegistry *registry;
+
+       g_return_if_fail (sd != NULL);
+       g_return_if_fail (E_IS_CAL_CLIENT (sd->target_client));
+       g_return_if_fail (sd->component != NULL);
+
+       while (!sd->send_activity) {
+               /* Give the main thread a chance to set this object
+                  and give it a 50 milliseconds delay too */
+               g_thread_yield ();
+               g_usleep (50000);
+       }
+
+       switch (icalcomponent_isa (sd->component)) {
+               case ICAL_VEVENT_COMPONENT:
+                       alert_ident = "calendar:failed-send-event";
+                       break;
+               case ICAL_VJOURNAL_COMPONENT:
+                       alert_ident = "calendar:failed-send-memo";
+                       break;
+               case ICAL_VTODO_COMPONENT:
+                       alert_ident = "calendar:failed-send-task";
+                       break;
+               default:
+                       g_warning ("%s: Cannot send component of kind %d", G_STRFUNC, icalcomponent_isa 
(sd->component));
+                       sd->success = FALSE;
+                       sd->alert_ident = g_strdup ("calendar:failed-send-event");
+                       return;
+       }
+
+       g_free (sd->alert_ident);
+       sd->alert_ident = g_strdup (alert_ident);
+
+       e_alert_sink_thread_job_set_alert_ident (job_data, alert_ident);
+
+       flags = e_comp_editor_get_flags (sd->comp_editor);
+       registry = e_shell_get_registry (sd->comp_editor->priv->shell);
+
+       if (sd->recur_mod == E_CAL_OBJ_MOD_ALL && e_cal_component_is_instance (sd->send_comp)) {
+               /* Ensure we send the master object, not the instance only */
+               icalcomponent *icalcomp = NULL;
+               const gchar *uid = NULL;
+
+               e_cal_component_get_uid (sd->send_comp, &uid);
+               if (e_cal_client_get_object_sync (sd->target_client, uid, NULL, &icalcomp, cancellable, NULL) 
&&
+                   icalcomp != NULL) {
+                       send_comp = e_cal_component_new_from_icalcomponent (icalcomp);
+               }
+       }
+
+       if (!send_comp)
+               send_comp = e_cal_component_clone (sd->send_comp);
+
+       cal_comp_util_copy_new_attendees (send_comp, sd->send_comp);
+
+       /* The user updates the delegated status to the Organizer,
+        * so remove all other attendees. */
+       if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) {
+               gchar *address;
+
+               address = itip_get_comp_attendee (registry, send_comp, sd->target_client);
+
+               if (address) {
+                       ece_set_attendees_for_delegation (send_comp, address);
+                       g_free (address);
+               }
+       }
+
+       g_clear_object (&sd->send_comp);
+       sd->send_comp = send_comp;
+}
+
+static void
+ece_save_component_done (gpointer ptr)
+{
+       SaveData *sd = ptr;
+
+       g_return_if_fail (sd != NULL);
+       g_return_if_fail (E_IS_COMP_EDITOR (sd->comp_editor));
+
+       if (sd->success) {
+               ECalComponent *comp;
+               gboolean delegated, is_new_meeting;
+               gboolean only_new_attendees = FALSE;
+               gboolean strip_alarms = TRUE;
+               guint32 flags;
+
+               if (sd->object_created)
+                       g_signal_emit (sd->comp_editor, signals[OBJECT_CREATED], 0, NULL);
+
+               comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (sd->component));
+               if (sd->comp_editor->priv->page_general) {
+                       GSList *added_attendees;
+
+                       added_attendees = e_comp_editor_page_general_get_added_attendees 
(sd->comp_editor->priv->page_general);
+                       cal_comp_util_set_added_attendees_mails (comp, added_attendees);
+               }
+
+               flags = e_comp_editor_get_flags (sd->comp_editor);
+               is_new_meeting = (flags & E_COMP_EDITOR_FLAG_WITH_ATTENDEES) == 0 ||
+                       (flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0;
+               delegated = (flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0 &&
+                       e_cal_client_check_save_schedules (sd->target_client);
+
+               if (delegated || (sd->with_send && e_cal_dialogs_send_component (
+                       GTK_WINDOW (sd->comp_editor), sd->target_client, comp,
+                       is_new_meeting, &strip_alarms, &only_new_attendees))) {
+                       ESourceRegistry *registry;
+                       EActivity *activity;
+
+                       registry = e_shell_get_registry (sd->comp_editor->priv->shell);
+
+                       if (delegated)
+                               only_new_attendees = FALSE;
+
+                       if ((itip_organizer_is_user (registry, comp, sd->target_client) ||
+                            itip_sentby_is_user (registry, comp, sd->target_client))) {
+                               if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL)
+                                       sd->first_send = E_CAL_COMPONENT_METHOD_PUBLISH;
+                               else
+                                       sd->first_send = E_CAL_COMPONENT_METHOD_REQUEST;
+                       } else {
+                               sd->first_send = E_CAL_COMPONENT_METHOD_REQUEST;
+
+                               if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0)
+                                       sd->second_send = E_CAL_COMPONENT_METHOD_REPLY;
+                       }
+
+                       sd->mime_attach_list = ece_get_mime_attach_list (sd->comp_editor);
+                       sd->strip_alarms = strip_alarms;
+                       sd->only_new_attendees = only_new_attendees;
+                       sd->send_comp = comp;
+                       sd->success = FALSE;
+                       sd->alert_ident = g_strdup ("calendar:failed-send-event");
+                       sd->alert_arg_0 = e_util_get_source_full_name (registry, e_client_get_source 
(E_CLIENT (sd->target_client)));
+
+                       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (sd->comp_editor),
+                               _("Sending notifications to attendees..."), sd->alert_ident, sd->alert_arg_0,
+                               ece_prepare_send_component_thread, sd, ece_prepare_send_component_done);
+
+                       if (activity)
+                               e_activity_bar_set_activity (sd->comp_editor->priv->activity_bar, activity);
+
+                       /* The thread is waiting for this to be set first */
+                       sd->send_activity = activity;
+
+                       return;
+               }
+
+               g_clear_object (&comp);
+       }
+
+       save_data_free (sd);
+}
+
+static void
+ece_gather_tzids_cb (icalparameter *param,
+                    gpointer user_data)
+{
+       GHashTable *tzids = user_data;
+       const gchar *tzid;
+
+       g_return_if_fail (param != NULL);
+       g_return_if_fail (tzids != NULL);
+
+       tzid = icalparameter_get_tzid (param);
+       if (tzid && *tzid && g_ascii_strcasecmp (tzid, "UTC") != 0)
+               g_hash_table_insert (tzids, g_strdup (tzid), NULL);
+}
+
+static gboolean
+ece_save_component_add_timezones_sync (SaveData *sd,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       GHashTable *tzids;
+       GHashTableIter iter;
+       gpointer key, value;
+       gboolean source_is_target;
+
+       g_return_val_if_fail (sd != NULL, FALSE);
+       g_return_val_if_fail (sd->component != NULL, FALSE);
+       g_return_val_if_fail (sd->target_client != NULL, FALSE);
+
+       sd->success = TRUE;
+
+       tzids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+       source_is_target = !sd->source_client ||
+               e_source_equal (e_client_get_source (E_CLIENT (sd->target_client)),
+                               e_client_get_source (E_CLIENT (sd->source_client)));
+
+       icalcomponent_foreach_tzid (sd->component, ece_gather_tzids_cb, tzids);
+
+       g_hash_table_iter_init (&iter, tzids);
+       while (sd->success && g_hash_table_iter_next (&iter, &key, &value)) {
+               const gchar *tzid = key;
+               icaltimezone *zone = NULL;
+               GError *local_error = NULL;
+
+               if (!e_cal_client_get_timezone_sync (source_is_target ? sd->target_client : sd->source_client,
+                       tzid, &zone, cancellable, &local_error)) {
+                       zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+                       if (!zone)
+                               zone = icaltimezone_get_builtin_timezone (tzid);
+                       if (!zone) {
+                               g_propagate_error (error, local_error);
+                               local_error = NULL;
+                               sd->success = FALSE;
+                               break;
+                       }
+               }
+
+               sd->success = e_cal_client_add_timezone_sync (sd->target_client, zone, cancellable, error);
+
+               g_clear_error (&local_error);
+       }
+
+       g_hash_table_destroy (tzids);
+
+       return sd->success;
+}
+
+static void
+ece_save_component_thread (EAlertSinkThreadJobData *job_data,
+                          gpointer user_data,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       SaveData *sd = user_data;
+       const gchar *create_alert_ident, *modify_alert_ident, *remove_alert_ident, *get_alert_ident;
+       gchar *orig_uid;
+
+       g_return_if_fail (sd != NULL);
+       g_return_if_fail (E_IS_CAL_CLIENT (sd->target_client));
+       g_return_if_fail (sd->component != NULL);
+
+       switch (icalcomponent_isa (sd->component)) {
+               case ICAL_VEVENT_COMPONENT:
+                       create_alert_ident = "calendar:failed-create-event";
+                       modify_alert_ident = "calendar:failed-modify-event";
+                       remove_alert_ident = "calendar:failed-remove-event";
+                       get_alert_ident = "calendar:failed-get-event";
+                       break;
+               case ICAL_VJOURNAL_COMPONENT:
+                       create_alert_ident = "calendar:failed-create-memo";
+                       modify_alert_ident = "calendar:failed-modify-memo";
+                       remove_alert_ident = "calendar:failed-remove-memo";
+                       get_alert_ident = "calendar:failed-get-memo";
+                       break;
+               case ICAL_VTODO_COMPONENT:
+                       create_alert_ident = "calendar:failed-create-task";
+                       modify_alert_ident = "calendar:failed-modify-task";
+                       remove_alert_ident = "calendar:failed-remove-task";
+                       get_alert_ident = "calendar:failed-get-task";
+                       break;
+               default:
+                       g_warning ("%s: Cannot save component of kind %d", G_STRFUNC, icalcomponent_isa 
(sd->component));
+                       return;
+       }
+
+       sd->success = ece_save_component_add_timezones_sync (sd, cancellable, error);
+       if (!sd->success) {
+               e_alert_sink_thread_job_set_alert_ident (job_data, "calendar:failed-add-timezone");
+               return;
+       }
+
+       orig_uid = g_strdup (icalcomponent_get_uid (sd->component));
+
+       if (cal_comp_is_icalcomp_on_server_sync (sd->component, sd->target_client, cancellable, error)) {
+               ECalComponent *comp;
+               gboolean has_recurrences;
+
+               e_alert_sink_thread_job_set_alert_ident (job_data, modify_alert_ident);
+
+               comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (sd->component));
+               g_return_if_fail (comp != NULL);
+
+               has_recurrences = e_cal_util_component_has_recurrences (sd->component);
+
+               if (has_recurrences && sd->recur_mod == E_CAL_OBJ_MOD_ALL)
+                       sd->success = comp_util_sanitize_recurrence_master_sync (comp, sd->target_client, 
cancellable, error);
+               else
+                       sd->success = TRUE;
+
+               if (sd->recur_mod == E_CAL_OBJ_MOD_THIS) {
+                       e_cal_component_set_rdate_list (comp, NULL);
+                       e_cal_component_set_rrule_list (comp, NULL);
+                       e_cal_component_set_exdate_list (comp, NULL);
+                       e_cal_component_set_exrule_list (comp, NULL);
+               }
+
+               sd->success = sd->success && e_cal_client_modify_object_sync (
+                       sd->target_client, e_cal_component_get_icalcomponent (comp), sd->recur_mod, 
cancellable, error);
+
+               g_clear_object (&comp);
+       } else {
+               gchar *uid = NULL;
+
+               e_alert_sink_thread_job_set_alert_ident (job_data, create_alert_ident);
+
+               sd->success = e_cal_client_create_object_sync (sd->target_client, sd->component, &uid, 
cancellable, error);
+
+               if (sd->success) {
+                       icalcomponent_set_uid (sd->component, uid);
+                       g_free (uid);
+
+                       sd->object_created = TRUE;
+               }
+       }
+
+       if (sd->success && sd->source_client &&
+           !e_source_equal (e_client_get_source (E_CLIENT (sd->target_client)),
+                            e_client_get_source (E_CLIENT (sd->source_client))) &&
+           cal_comp_is_icalcomp_on_server_sync (sd->component, sd->source_client, cancellable, NULL)) {
+               ECalObjModType recur_mod = E_CAL_OBJ_MOD_THIS;
+
+               /* Comp found a new home. Remove it from old one. */
+               if (e_cal_util_component_is_instance (sd->component) ||
+                   e_cal_util_component_has_recurrences (sd->component))
+                       recur_mod = E_CAL_OBJ_MOD_ALL;
+
+               sd->success = e_cal_client_remove_object_sync (
+                       sd->source_client, orig_uid,
+                       NULL, recur_mod, cancellable, error);
+
+               if (!sd->success) {
+                       gchar *source_display_name;
+
+                       source_display_name = e_util_get_source_full_name (e_shell_get_registry 
(sd->comp_editor->priv->shell),
+                               e_client_get_source (E_CLIENT (sd->source_client)));
+
+                       e_alert_sink_thread_job_set_alert_ident (job_data, remove_alert_ident);
+                       e_alert_sink_thread_job_set_alert_arg_0 (job_data, source_display_name);
+
+                       g_free (source_display_name);
+               }
+       }
+
+       g_free (orig_uid);
+
+       if (sd->success && !sd->close_after_save) {
+               icalcomponent *comp = NULL;
+               gchar *uid, *rid = NULL;
+
+               uid = g_strdup (icalcomponent_get_uid (sd->component));
+               if (icalcomponent_get_first_property (sd->component, ICAL_RECURRENCEID_PROPERTY)) {
+                       struct icaltimetype ridtt;
+
+                       ridtt = icalcomponent_get_recurrenceid (sd->component);
+                       if (icaltime_is_valid_time (ridtt) && !icaltime_is_null_time (ridtt)) {
+                               rid = icaltime_as_ical_string_r (ridtt);
+                       }
+               }
+
+               sd->success = e_cal_client_get_object_sync (sd->target_client, uid, rid, &comp, cancellable, 
error);
+               if (sd->success && comp) {
+                       icalcomponent_free (sd->component);
+                       sd->component = comp;
+               } else {
+                       e_alert_sink_thread_job_set_alert_ident (job_data, get_alert_ident);
+               }
+
+               g_free (uid);
+               g_free (rid);
+       }
+}
+
+static void
+ece_save_component (ECompEditor *comp_editor,
+                   icalcomponent *component,
+                   gboolean with_send,
+                   gboolean close_after_save)
+{
+       EActivity *activity;
+       const gchar *summary;
+       ECalObjModType recur_mod = E_CAL_OBJ_MOD_THIS;
+       SaveData *sd;
+       gchar *source_display_name;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (component != NULL);
+
+       summary = icalcomponent_get_summary (component);
+       if (!summary || !*summary) {
+               if (!e_cal_dialogs_send_component_prompt_subject (GTK_WINDOW (comp_editor), component)) {
+                       return;
+               }
+       }
+
+       if (e_cal_util_component_is_instance (component)) {
+               if (!e_cal_dialogs_recur_icalcomp (comp_editor->priv->target_client,
+                       component, &recur_mod, GTK_WINDOW (comp_editor), FALSE)) {
+                       return;
+               }
+       } else if (e_cal_util_component_has_recurrences (component)) {
+               recur_mod = E_CAL_OBJ_MOD_ALL;
+       }
+
+       e_comp_editor_enable (comp_editor, FALSE);
+
+       sd = g_new0 (SaveData, 1);
+       sd->comp_editor = g_object_ref (comp_editor);
+       sd->source_client = comp_editor->priv->source_client ? g_object_ref 
(comp_editor->priv->source_client) : NULL;
+       sd->target_client = g_object_ref (comp_editor->priv->target_client);
+       sd->component = icalcomponent_new_clone (component);
+       sd->with_send = with_send;
+       sd->close_after_save = close_after_save;
+       sd->recur_mod = recur_mod;
+       sd->first_send = E_CAL_COMPONENT_METHOD_NONE;
+       sd->second_send = E_CAL_COMPONENT_METHOD_NONE;
+       sd->success = FALSE;
+
+       source_display_name = e_util_get_source_full_name (e_shell_get_registry (comp_editor->priv->shell),
+               e_client_get_source (E_CLIENT (sd->target_client)));
+
+       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (comp_editor),
+               _("Saving changes..."), "calendar:failed-create-event", source_display_name,
+               ece_save_component_thread, sd, ece_save_component_done);
+
+       if (activity)
+               e_activity_bar_set_activity (comp_editor->priv->activity_bar, activity);
+
+       g_clear_object (&activity);
+       g_free (source_display_name);
+}
+
+typedef struct _OpenTargetClientData {
+       ECompEditor *comp_editor;
+       ESource *source;
+       gchar *extension_name;
+       EClient *client;
+       gchar *cal_email_address;
+       gchar *alarm_email_address;
+       gboolean is_target_client_change;
+       EActivity *activity;
+} OpenTargetClientData;
+
+static void
+open_target_client_data_free (gpointer ptr)
+{
+       OpenTargetClientData *otc = ptr;
+
+       if (otc) {
+               if (otc->comp_editor) {
+                       if (otc->client) {
+                               e_comp_editor_set_alarm_email_address (otc->comp_editor, 
otc->alarm_email_address);
+                               e_comp_editor_set_cal_email_address (otc->comp_editor, 
otc->cal_email_address);
+                               e_comp_editor_set_target_client (otc->comp_editor, E_CAL_CLIENT 
(otc->client));
+
+                               if (otc->is_target_client_change)
+                                       e_comp_editor_set_changed (otc->comp_editor, TRUE);
+                       }
+
+                       if (otc->comp_editor->priv->activity_bar && otc->activity) {
+                               if (otc->activity == e_activity_bar_get_activity 
(otc->comp_editor->priv->activity_bar))
+                                       e_activity_bar_set_activity (otc->comp_editor->priv->activity_bar, 
NULL);
+
+                               if (otc->activity == otc->comp_editor->priv->target_client_opening)
+                                       g_clear_object (&otc->comp_editor->priv->target_client_opening);
+                       }
+
+                       if (otc->source) {
+                               EShell *shell;
+                               ECredentialsPrompter *credentials_prompter;
+
+                               shell = e_comp_editor_get_shell (otc->comp_editor);
+                               credentials_prompter = e_shell_get_credentials_prompter (shell);
+
+                               e_credentials_prompter_set_auto_prompt_disabled_for (credentials_prompter, 
otc->source, TRUE);
+                       }
+
+                       e_comp_editor_sensitize_widgets (otc->comp_editor);
+               }
+
+               g_clear_object (&otc->comp_editor);
+               g_clear_object (&otc->source);
+               g_clear_object (&otc->client);
+               g_clear_object (&otc->activity);
+               g_free (otc->extension_name);
+               g_free (otc->cal_email_address);
+               g_free (otc->alarm_email_address);
+               g_free (otc);
+       }
+}
+
+static void
+comp_editor_open_target_client_thread (EAlertSinkThreadJobData *job_data,
+                                      gpointer user_data,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       OpenTargetClientData *otc = user_data;
+       EClientCache *client_cache;
+
+       g_return_if_fail (otc != NULL);
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (otc->comp_editor));
+       g_return_if_fail (E_IS_SOURCE (otc->source));
+       g_return_if_fail (otc->extension_name != NULL);
+
+       client_cache = e_shell_get_client_cache (e_comp_editor_get_shell (otc->comp_editor));
+
+       otc->client = e_client_cache_get_client_sync (client_cache, otc->source, otc->extension_name,
+               30, cancellable, error);
+
+       if (otc->client) {
+               /* Cache some properties which require remote calls */
+
+               if (!g_cancellable_is_cancelled (cancellable)) {
+                       e_client_get_capabilities (otc->client);
+               }
+
+               if (!g_cancellable_is_cancelled (cancellable)) {
+                       e_client_get_backend_property_sync (otc->client,
+                               CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
+                               &otc->cal_email_address,
+                               cancellable, error);
+               }
+
+               if (!g_cancellable_is_cancelled (cancellable)) {
+                       e_client_get_backend_property_sync (otc->client,
+                               CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS,
+                               &otc->alarm_email_address,
+                               cancellable, error);
+               }
+
+               if (g_cancellable_is_cancelled (cancellable))
+                       g_clear_object (&otc->client);
+       }
+}
+
+typedef struct _UpdateActivityBarData {
+       ECompEditor *comp_editor;
+       EActivity *activity;
+} UpdateActivityBarData;
+
+static void
+update_activity_bar_data_free (gpointer ptr)
+{
+       UpdateActivityBarData *uab = ptr;
+
+       if (uab) {
+               g_clear_object (&uab->comp_editor);
+               g_clear_object (&uab->activity);
+               g_free (uab);
+       }
+}
+
+static gboolean
+update_activity_bar_cb (gpointer user_data)
+{
+       UpdateActivityBarData *uab = user_data;
+
+       g_return_val_if_fail (uab != NULL, FALSE);
+       g_return_val_if_fail (E_IS_COMP_EDITOR (uab->comp_editor), FALSE);
+       g_return_val_if_fail (E_IS_ACTIVITY (uab->activity), FALSE);
+
+       if (uab->comp_editor->priv->target_client_opening == uab->activity &&
+           e_activity_get_state (uab->activity) != E_ACTIVITY_CANCELLED &&
+           e_activity_get_state (uab->activity) != E_ACTIVITY_COMPLETED) {
+               e_activity_bar_set_activity (uab->comp_editor->priv->activity_bar, uab->activity);
+       }
+
+       return FALSE;
+}
+
+static void
+e_comp_editor_open_target_client (ECompEditor *comp_editor)
+{
+       OpenTargetClientData *otc;
+       ESource *source;
+       EActivity *activity;
+       ECredentialsPrompter *credentials_prompter;
+       gchar *source_display_name, *description = NULL, *alert_ident = NULL, *alert_arg_0 = NULL;
+       gboolean is_target_client_change;
+       const gchar *extension_name;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (comp_editor->priv->page_general != NULL);
+
+       source = e_comp_editor_page_general_ref_selected_source (comp_editor->priv->page_general);
+       if (!source)
+               return;
+
+       if (comp_editor->priv->target_client &&
+           e_client_get_source (E_CLIENT (comp_editor->priv->target_client)) == source) {
+               g_clear_object (&source);
+               return;
+       }
+
+       if (comp_editor->priv->target_client_opening) {
+               e_activity_cancel (comp_editor->priv->target_client_opening);
+               g_clear_object (&comp_editor->priv->target_client_opening);
+       }
+
+       is_target_client_change = comp_editor->priv->target_client != NULL;
+       g_clear_object (&comp_editor->priv->target_client);
+
+       extension_name = e_comp_editor_page_general_get_source_extension_name 
(comp_editor->priv->page_general);
+       source_display_name = e_util_get_source_full_name (
+               e_shell_get_registry (e_comp_editor_get_shell (comp_editor)),
+               source);
+
+       g_return_if_fail (e_util_get_open_source_job_info (extension_name, source_display_name,
+               &description, &alert_ident, &alert_arg_0));
+
+       credentials_prompter = e_shell_get_credentials_prompter (e_comp_editor_get_shell (comp_editor));
+       e_credentials_prompter_set_auto_prompt_disabled_for (credentials_prompter, source, FALSE);
+
+       otc = g_new0 (OpenTargetClientData, 1);
+       otc->extension_name = g_strdup (extension_name);
+       otc->comp_editor = g_object_ref (comp_editor);
+       otc->source = g_object_ref (source);
+       otc->is_target_client_change = is_target_client_change;
+
+       activity = e_alert_sink_submit_thread_job (
+               E_ALERT_SINK (comp_editor), description, alert_ident, alert_arg_0,
+               comp_editor_open_target_client_thread, otc,
+               open_target_client_data_free);
+
+       otc->activity = g_object_ref (activity);
+       comp_editor->priv->target_client_opening = g_object_ref (activity);
+
+       /* Close all alerts */
+       while (e_alert_bar_close_alert (comp_editor->priv->alert_bar)) {
+               ;
+       }
+
+       if (comp_editor->priv->activity_bar) {
+               UpdateActivityBarData *uab;
+
+               uab = g_new0 (UpdateActivityBarData, 1);
+               uab->comp_editor = g_object_ref (comp_editor);
+               uab->activity = g_object_ref (activity);
+
+               /* To avoid UI flickering when the source can be opened quickly */
+               g_timeout_add_seconds_full (G_PRIORITY_LOW, 1,
+                       update_activity_bar_cb, uab, update_activity_bar_data_free);
+       }
+
+       g_free (description);
+       g_free (alert_ident);
+       g_free (alert_arg_0);
+       g_free (source_display_name);
+       g_clear_object (&source);
+       g_clear_object (&activity);
+}
+
+static void
+e_comp_editor_update_window_title (ECompEditor *comp_editor)
+{
+       ECompEditorClass *comp_editor_class;
+       gboolean with_attendees = FALSE;
+       const gchar *format, *title_suffix;
+       gchar *title;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (comp_editor->priv->page_general)
+               with_attendees = e_comp_editor_page_general_get_show_attendees 
(comp_editor->priv->page_general);
+
+       comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
+       if (with_attendees)
+               format = comp_editor_class->title_format_with_attendees;
+       else
+               format = comp_editor_class->title_format_without_attendees;
+
+       title_suffix = e_comp_editor_get_title_suffix (comp_editor);
+
+       title = g_strdup_printf (format, title_suffix && *title_suffix ? title_suffix : _("No Summary"));
+
+       gtk_window_set_icon_name (GTK_WINDOW (comp_editor), comp_editor_class->icon_name);
+       gtk_window_set_title (GTK_WINDOW (comp_editor), title);
+
+       g_free (title);
+}
+
+static void
+e_comp_editor_close (ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       g_signal_emit (comp_editor, signals[EDITOR_CLOSED], 0, FALSE, NULL);
+
+       gtk_widget_destroy (GTK_WIDGET (comp_editor));
+}
+
+static void
+e_comp_editor_save_and_close (ECompEditor *comp_editor,
+                             gboolean can_close)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (comp_editor->priv->component) {
+               icalcomponent *component = icalcomponent_new_clone (comp_editor->priv->component);
+               if (component && e_comp_editor_fill_component (comp_editor, component)) {
+                       ece_save_component (comp_editor, component, TRUE, can_close);
+                       icalcomponent_free (component);
+               }
+       }
+}
+
+static GtkResponseType
+ece_save_component_dialog (ECompEditor *comp_editor)
+{
+       const icalcomponent *component;
+       GtkWindow *parent;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), GTK_RESPONSE_NO);
+       g_return_val_if_fail (e_comp_editor_get_component (comp_editor) != NULL, GTK_RESPONSE_NO);
+
+       parent = GTK_WINDOW (comp_editor);
+       component = e_comp_editor_get_component (comp_editor);
+       switch (icalcomponent_isa (component)) {
+               case ICAL_VEVENT_COMPONENT:
+                       if (e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general))
+                               return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-meeting", 
NULL);
+                       else
+                               return e_alert_run_dialog_for_args (parent, 
"calendar:prompt-save-appointment", NULL);
+               case ICAL_VTODO_COMPONENT:
+                       return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-task", NULL);
+               case ICAL_VJOURNAL_COMPONENT:
+                       return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-memo", NULL);
+               default:
+                       return GTK_RESPONSE_NO;
+       }
+}
+
+static gboolean
+e_comp_editor_prompt_and_save_changes (ECompEditor *comp_editor,
+                                      gboolean with_send)
+{
+       icalcomponent *component;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+
+       if (!e_comp_editor_get_changed (comp_editor))
+               return TRUE;
+
+       switch (ece_save_component_dialog (comp_editor)) {
+       case GTK_RESPONSE_YES: /* Save */
+               if (e_client_is_readonly (E_CLIENT (comp_editor->priv->target_client))) {
+                       e_alert_submit (
+                               E_ALERT_SINK (comp_editor),
+                               "calendar:prompt-read-only-cal-editor",
+                               e_source_get_display_name (
+                                       e_client_get_source (E_CLIENT (comp_editor->priv->target_client))),
+                               NULL);
+                       /* don't discard changes when selected readonly calendar */
+                       return FALSE;
+               }
+
+               if (comp_editor->priv->component &&
+                   e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general) &&
+                   icalcomponent_isa (comp_editor->priv->component) == ICAL_VTODO_COMPONENT
+                   && e_client_check_capability (E_CLIENT (comp_editor->priv->target_client), 
CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) {
+                       e_alert_submit (
+                               E_ALERT_SINK (comp_editor),
+                               "calendar:prompt-no-task-assignment-editor",
+                               e_source_get_display_name (
+                                       e_client_get_source (E_CLIENT (comp_editor->priv->target_client))),
+                               NULL);
+                       return FALSE;
+               }
+
+               component = icalcomponent_new_clone (comp_editor->priv->component);
+               if (!e_comp_editor_fill_component (comp_editor, component)) {
+                       icalcomponent_free (component);
+                       return FALSE;
+               }
+
+               ece_save_component (comp_editor, component, with_send, TRUE);
+
+               return FALSE;
+       case GTK_RESPONSE_NO: /* Discard */
+               return TRUE;
+       case GTK_RESPONSE_CANCEL: /* Cancel */
+       default:
+               return FALSE;
+       }
+
+       return FALSE;
+}
+
+static void
+action_close_cb (GtkAction *action,
+                 ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (e_comp_editor_prompt_and_save_changes (comp_editor, TRUE))
+               e_comp_editor_close (comp_editor);
+}
+
+static void
+action_help_cb (GtkAction *action,
+                ECompEditor *comp_editor)
+{
+       ECompEditorClass *klass;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       klass = E_COMP_EDITOR_GET_CLASS (comp_editor);
+       g_return_if_fail (klass->help_section != NULL);
+
+       e_display_help (GTK_WINDOW (comp_editor), klass->help_section);
+}
+
+static void
+ece_print_or_preview (ECompEditor *comp_editor,
+                     GtkPrintOperationAction print_action)
+{
+       icalcomponent *component;
+       ECalComponent *comp;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (e_comp_editor_get_component (comp_editor) != NULL);
+
+       component = icalcomponent_new_clone ((icalcomponent *) e_comp_editor_get_component (comp_editor));
+       if (!e_comp_editor_fill_component (comp_editor, component)) {
+               icalcomponent_free (component);
+               return;
+       }
+
+       comp = e_cal_component_new_from_icalcomponent (component);
+       g_return_if_fail (comp != NULL);
+
+       print_comp (comp,
+               e_comp_editor_get_target_client (comp_editor),
+               calendar_config_get_icaltimezone (),
+               calendar_config_get_24_hour_format (),
+               print_action);
+
+       g_object_unref (comp);
+}
+
+static void
+action_print_cb (GtkAction *action,
+                 ECompEditor *comp_editor)
+{
+       ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
+}
+
+static void
+action_print_preview_cb (GtkAction *action,
+                         ECompEditor *comp_editor)
+{
+       ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PREVIEW);
+}
+
+static void
+action_save_cb (GtkAction *action,
+                ECompEditor *comp_editor)
+{
+       e_comp_editor_save_and_close (comp_editor, FALSE);
+}
+
+static void
+action_save_and_close_cb (GtkAction *action,
+                          ECompEditor *comp_editor)
+{
+       e_comp_editor_save_and_close (comp_editor, TRUE);
+}
+
+static gboolean
+ece_organizer_email_address_is_user (ECompEditor *comp_editor,
+                                    EClient *client,
+                                    const gchar *email_address,
+                                    gboolean is_organizer)
+{
+       ESourceRegistry *registry;
+       const gchar *cal_email_address;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+
+       email_address = itip_strip_mailto (email_address);
+
+       if (!email_address || !*email_address)
+               return FALSE;
+
+       cal_email_address = e_comp_editor_get_cal_email_address (comp_editor);
+       if (cal_email_address && *cal_email_address &&
+           g_ascii_strcasecmp (cal_email_address, email_address) == 0) {
+               return TRUE;
+       }
+
+       if (is_organizer && e_client_check_capability (client, 
CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS))
+               return FALSE;
+
+       registry = e_shell_get_registry (e_comp_editor_get_shell (comp_editor));
+
+       return itip_address_is_user (registry, email_address);
+}
+
+static gboolean
+ece_organizer_is_user (ECompEditor *comp_editor,
+                      icalcomponent *component,
+                      EClient *client)
+{
+       icalproperty *prop;
+       const gchar *organizer;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+       g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+
+       prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY);
+       if (!prop || e_client_check_capability (client, CAL_STATIC_CAPABILITY_NO_ORGANIZER))
+               return FALSE;
+
+       organizer = itip_strip_mailto (icalproperty_get_organizer (prop));
+       if (!organizer || !*organizer)
+               return FALSE;
+
+       return ece_organizer_email_address_is_user (comp_editor, client, organizer, TRUE);
+}
+
+static gboolean
+ece_sentby_is_user (ECompEditor *comp_editor,
+                   icalcomponent *component,
+                   EClient *client)
+{
+       icalproperty *prop;
+       icalparameter *param;
+       const gchar *sentby;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+       g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+
+       prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY);
+       if (!prop || e_client_check_capability (client, CAL_STATIC_CAPABILITY_NO_ORGANIZER))
+               return FALSE;
+
+       param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+       if (!param)
+               return FALSE;
+
+       sentby = icalparameter_get_sentby (param);
+
+       return ece_organizer_email_address_is_user (comp_editor, client, sentby, FALSE);
+}
+
+static void
+ece_emit_times_changed_cb (ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       g_signal_emit (comp_editor, signals[TIMES_CHANGED], 0, NULL);
+}
+
+static void
+ece_connect_time_parts (ECompEditor *comp_editor,
+                       ECompEditorPropertyPart *dtstart_part,
+                       ECompEditorPropertyPart *dtend_part)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+#define update_part(x) G_STMT_START { \
+       if (x) \
+               g_object_ref (x); \
+       if (comp_editor->priv->x) { \
+               g_signal_handlers_disconnect_by_func (comp_editor->priv->x, G_CALLBACK 
(ece_emit_times_changed_cb), comp_editor); \
+               g_clear_object (&comp_editor->priv->x); \
+       } \
+       if (x) { \
+               comp_editor->priv->x = x; \
+               g_signal_connect_swapped (comp_editor->priv->x, "changed", \
+                       G_CALLBACK (ece_emit_times_changed_cb), comp_editor); \
+       } \
+       } G_STMT_END
+
+       update_part (dtstart_part);
+       update_part (dtend_part);
+
+#undef update_part
+}
+
+static void
+ece_sensitize_widgets (ECompEditor *comp_editor,
+                      gboolean force_insensitive)
+{
+       GtkActionGroup *group;
+       GSList *link;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
+               ECompEditorPage *page = link->data;
+
+               g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+               if (!E_IS_COMP_EDITOR_PAGE (page))
+                       continue;
+
+               e_comp_editor_page_sensitize_widgets (page, force_insensitive);
+       }
+
+       group = e_comp_editor_get_action_group (comp_editor, "individual");
+       gtk_action_group_set_sensitive (group, !force_insensitive);
+
+       group = e_comp_editor_get_action_group (comp_editor, "editable");
+       gtk_action_group_set_sensitive (group, !force_insensitive);
+}
+
+static void
+ece_fill_widgets (ECompEditor *comp_editor,
+                 icalcomponent *component)
+{
+       GSList *link;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (component != NULL);
+
+       for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
+               ECompEditorPage *page = link->data;
+
+               g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+               if (!E_IS_COMP_EDITOR_PAGE (page))
+                       continue;
+
+               e_comp_editor_page_fill_widgets (page, component);
+       }
+}
+
+static gboolean
+ece_fill_component (ECompEditor *comp_editor,
+                   icalcomponent *component)
+{
+       GSList *link;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
+               ECompEditorPage *page = link->data;
+
+               g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+               if (!E_IS_COMP_EDITOR_PAGE (page))
+                       continue;
+
+               if (!e_comp_editor_page_fill_component (page, component))
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void
+comp_editor_realize_cb (ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (comp_editor->priv->component) {
+               e_comp_editor_fill_widgets (comp_editor, comp_editor->priv->component);
+               e_comp_editor_set_changed (comp_editor, FALSE);
+       }
+
+       e_comp_editor_update_window_title (comp_editor);
+       e_comp_editor_sensitize_widgets (comp_editor);
+
+       if (comp_editor->priv->page_general && comp_editor->priv->origin_source) {
+               e_comp_editor_page_general_set_selected_source (
+                       comp_editor->priv->page_general,
+                       comp_editor->priv->origin_source);
+               e_comp_editor_set_changed (comp_editor, FALSE);
+       }
+
+       if (comp_editor->priv->page_general) {
+               e_comp_editor_page_general_update_view (comp_editor->priv->page_general);
+
+               if (!comp_editor->priv->show_attendees_handler_id) {
+                       comp_editor->priv->show_attendees_handler_id =
+                               e_signal_connect_notify_swapped (comp_editor->priv->page_general,
+                                       "notify::show-attendees",
+                                       G_CALLBACK (e_comp_editor_update_window_title), comp_editor);
+               }
+       }
+
+       if (!comp_editor->priv->target_client)
+               e_comp_editor_open_target_client (comp_editor);
+}
+
+static void
+comp_editor_unrealize_cb (ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (comp_editor->priv->page_general) {
+               e_signal_disconnect_notify_handler (comp_editor->priv->page_general,
+                       &comp_editor->priv->show_attendees_handler_id);
+       }
+}
+
+static gboolean
+comp_editor_delete_event (GtkWidget *widget,
+                         GdkEventAny *event)
+{
+       ECompEditor *comp_editor;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (widget), FALSE);
+
+       comp_editor = E_COMP_EDITOR (widget);
+
+       /* It's disabled when the component is being saved */
+       if (gtk_widget_get_sensitive (GTK_WIDGET (comp_editor->priv->content)))
+               action_close_cb (NULL, comp_editor);
+
+       return TRUE;
+}
+
+static gboolean
+comp_editor_key_press_event (GtkWidget *widget,
+                            GdkEventKey *event)
+{
+       ECompEditor *comp_editor;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (widget), FALSE);
+
+       comp_editor = E_COMP_EDITOR (widget);
+
+       if (event->keyval == GDK_KEY_Escape &&
+           !e_alert_bar_close_alert (comp_editor->priv->alert_bar)) {
+               GtkAction *action;
+
+               action = e_comp_editor_get_action (comp_editor, "close");
+               gtk_action_activate (action);
+
+               return TRUE;
+       }
+
+       /* Chain up to parent's method. */
+       return GTK_WIDGET_CLASS (e_comp_editor_parent_class)->key_press_event (widget, event);
+}
+
+static void
+comp_editor_selected_source_notify_cb (ECompEditorPageGeneral *page_general,
+                                      GParamSpec *param,
+                                      ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general));
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (comp_editor->priv->page_general == page_general);
+
+       e_comp_editor_open_target_client (comp_editor);
+}
+
+static void
+e_comp_editor_submit_alert (EAlertSink *alert_sink,
+                           EAlert *alert)
+{
+       ECompEditor *comp_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (alert_sink));
+       g_return_if_fail (E_IS_ALERT (alert));
+
+       comp_editor = E_COMP_EDITOR (alert_sink);
+
+       e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert);
+}
+
+static void
+e_comp_editor_set_origin_source (ECompEditor *comp_editor,
+                                ESource *origin_source)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       if (origin_source)
+               g_return_if_fail (E_IS_SOURCE (origin_source));
+
+       g_clear_object (&comp_editor->priv->origin_source);
+       if (origin_source)
+               comp_editor->priv->origin_source = g_object_ref (origin_source);
+}
+
+static void
+e_comp_editor_set_shell (ECompEditor *comp_editor,
+                        EShell *shell)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (E_IS_SHELL (shell));
+
+       g_clear_object (&comp_editor->priv->shell);
+       comp_editor->priv->shell = g_object_ref (shell);
+}
+
+static void
+e_comp_editor_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ALARM_EMAIL_ADDRESS:
+                       e_comp_editor_set_alarm_email_address (
+                               E_COMP_EDITOR (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_CAL_EMAIL_ADDRESS:
+                       e_comp_editor_set_cal_email_address (
+                               E_COMP_EDITOR (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_CHANGED:
+                       e_comp_editor_set_changed (
+                               E_COMP_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_COMPONENT:
+                       e_comp_editor_set_component (
+                               E_COMP_EDITOR (object),
+                               g_value_get_pointer (value));
+                       return;
+
+               case PROP_FLAGS:
+                       e_comp_editor_set_flags (
+                               E_COMP_EDITOR (object),
+                               g_value_get_uint (value));
+                       return;
+
+               case PROP_ORIGIN_SOURCE:
+                       e_comp_editor_set_origin_source (
+                               E_COMP_EDITOR (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_SHELL:
+                       e_comp_editor_set_shell (
+                               E_COMP_EDITOR (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_SOURCE_CLIENT:
+                       e_comp_editor_set_source_client (
+                               E_COMP_EDITOR (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_TARGET_CLIENT:
+                       e_comp_editor_set_target_client (
+                               E_COMP_EDITOR (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_TITLE_SUFFIX:
+                       e_comp_editor_set_title_suffix (
+                               E_COMP_EDITOR (object),
+                               g_value_get_string (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_ALARM_EMAIL_ADDRESS:
+                       g_value_set_string (
+                               value,
+                               e_comp_editor_get_alarm_email_address (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_CAL_EMAIL_ADDRESS:
+                       g_value_set_string (
+                               value,
+                               e_comp_editor_get_cal_email_address (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_CHANGED:
+                       g_value_set_boolean (
+                               value,
+                               e_comp_editor_get_changed (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_COMPONENT:
+                       g_value_set_pointer (
+                               value,
+                               (gpointer) e_comp_editor_get_component (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_FLAGS:
+                       g_value_set_uint (
+                               value,
+                               e_comp_editor_get_flags (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_ORIGIN_SOURCE:
+                       g_value_set_object (
+                               value,
+                               e_comp_editor_get_origin_source (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_SHELL:
+                       g_value_set_object (
+                               value,
+                               e_comp_editor_get_shell (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_SOURCE_CLIENT:
+                       g_value_set_object (
+                               value,
+                               e_comp_editor_get_source_client (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_TARGET_CLIENT:
+                       g_value_set_object (
+                               value,
+                               e_comp_editor_get_target_client (
+                               E_COMP_EDITOR (object)));
+                       return;
+
+               case PROP_TITLE_SUFFIX:
+                       g_value_set_string (
+                               value,
+                               e_comp_editor_get_title_suffix (
+                               E_COMP_EDITOR (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_comp_editor_constructed (GObject *object)
+{
+       const gchar *ui =
+               "<ui>"
+               "  <menubar action='main-menu'>"
+               "    <menu action='file-menu'>"
+               "      <menuitem action='save'/>"
+               "      <menuitem action='save-and-close'/>"
+               "      <separator/>"
+               "      <menuitem action='print-preview'/>"
+               "      <menuitem action='print'/>"
+               "      <separator/>"
+               "      <menuitem action='close'/>"
+               "    </menu>"
+               "    <menu action='edit-menu'>"
+               "      <menuitem action='undo'/>"
+               "      <menuitem action='redo'/>"
+               "      <separator/>"
+               "      <menuitem action='cut-clipboard'/>"
+               "      <menuitem action='copy-clipboard'/>"
+               "      <menuitem action='paste-clipboard'/>"
+               "      <menuitem action='delete-selection'/>"
+               "      <separator/>"
+               "      <menuitem action='select-all'/>"
+               "    </menu>"
+               "    <menu action='view-menu'>"
+               "      <placeholder name='parts'/>"
+               "      <separator />"
+               "      <placeholder name='columns'/>"
+               "    </menu>"
+               "    <menu action='insert-menu'/>"
+               "    <menu action='options-menu'>"
+               "      <placeholder name='tabs'/>"
+               "      <placeholder name='toggles'/>"
+               "    </menu>"
+               "    <menu action='help-menu'>"
+               "      <menuitem action='help'/>"
+               "    </menu>"
+               "  </menubar>"
+               "  <toolbar name='main-toolbar'>"
+               "    <toolitem action='save-and-close'/>\n"
+               "    <toolitem action='save'/>"
+               "    <toolitem action='print'/>"
+               "    <separator/>"
+               "    <toolitem action='undo'/>"
+               "    <toolitem action='redo'/>"
+               "    <separator/>"
+               "    <placeholder name='content'/>"
+               "    <placeholder name='after-content'/>"
+               "  </toolbar>"
+               "</ui>";
+
+       GtkActionEntry core_entries[] = {
+
+               { "close",
+                 "window-close",
+                 N_("_Close"),
+                 "<Control>w",
+                 N_("Close the current window"),
+                 G_CALLBACK (action_close_cb) },
+
+               { "copy-clipboard",
+                 "edit-copy",
+                 N_("_Copy"),
+                 "<Control>c",
+                 N_("Copy the selection"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               { "cut-clipboard",
+                 "edit-cut",
+                 N_("Cu_t"),
+                 "<Control>x",
+                 N_("Cut the selection"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               { "delete-selection",
+                 "edit-delete",
+                 N_("_Delete"),
+                 NULL,
+                 N_("Delete the selection"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               { "help",
+                 "help-browser",
+                 N_("_Help"),
+                 "F1",
+                 N_("View help"),
+                 G_CALLBACK (action_help_cb) },
+
+               { "paste-clipboard",
+                 "edit-paste",
+                 N_("_Paste"),
+                 "<Control>v",
+                 N_("Paste the clipboard"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               { "print",
+                 "document-print",
+                 N_("_Print..."),
+                 "<Control>p",
+                 NULL,
+                 G_CALLBACK (action_print_cb) },
+
+               { "print-preview",
+                 "document-print-preview",
+                 N_("Pre_view..."),
+                 NULL,
+                 NULL,
+                 G_CALLBACK (action_print_preview_cb) },
+
+               { "select-all",
+                 "edit-select-all",
+                 N_("Select _All"),
+                 "<Control>a",
+                 N_("Select all text"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               { "undo",
+                 "edit-undo",
+                 N_("_Undo"),
+                 "<Control>z",
+                 N_("Undo"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               { "redo",
+                 "edit-redo",
+                 N_("_Redo"),
+                 "<Control>y",
+                 N_("Redo"),
+                 NULL },  /* Handled by EFocusTracker */
+
+               /* Menus */
+
+               { "classification-menu",
+                 NULL,
+                 N_("_Classification"),
+                 NULL,
+                 NULL,
+                 NULL },
+
+               { "edit-menu",
+                 NULL,
+                 N_("_Edit"),
+                 NULL,
+                 NULL,
+                 NULL },
+
+               { "file-menu",
+                 NULL,
+                 N_("_File"),
+                 NULL,
+                 NULL,
+                 NULL },
+
+               { "help-menu",
+                 NULL,
+                 N_("_Help"),
+                 NULL,
+                 NULL,
+                 NULL },
+
+               { "insert-menu",
+                 NULL,
+                 N_("_Insert"),
+                 NULL,
+                 NULL,
+                 NULL },
+
+               { "options-menu",
+                 NULL,
+                 N_("_Options"),
+                 NULL,
+                 NULL,
+                 NULL },
+
+               { "view-menu",
+                 NULL,
+                 N_("_View"),
+                 NULL,
+                 NULL,
+                 NULL }
+       };
+
+       GtkActionEntry editable_entries[] = {
+
+               { "save",
+                 "document-save",
+                 N_("_Save"),
+                 "<Control>s",
+                 N_("Save current changes"),
+                 G_CALLBACK (action_save_cb) },
+
+               { "save-and-close",
+                 NULL,
+                 N_("Save and Close"),
+                 NULL,
+                 N_("Save current changes and close editor"),
+                 G_CALLBACK (action_save_and_close_cb) }
+       };
+
+       ECompEditor *comp_editor = E_COMP_EDITOR (object);
+       GtkWidget *widget;
+       GtkBox *vbox;
+       GtkAction *action;
+       GtkActionGroup *action_group;
+       EFocusTracker *focus_tracker;
+       GError *error = NULL;
+
+       G_OBJECT_CLASS (e_comp_editor_parent_class)->constructed (object);
+
+       comp_editor->priv->calendar_settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+       comp_editor->priv->ui_manager = gtk_ui_manager_new ();
+
+       gtk_window_add_accel_group (
+               GTK_WINDOW (comp_editor),
+               gtk_ui_manager_get_accel_group (comp_editor->priv->ui_manager));
+
+       /* Setup Action Groups */
+
+       action_group = gtk_action_group_new ("individual");
+       gtk_action_group_set_translation_domain (
+               action_group, GETTEXT_PACKAGE);
+       gtk_ui_manager_insert_action_group (
+               comp_editor->priv->ui_manager, action_group, 0);
+       g_object_unref (action_group);
+
+       action_group = gtk_action_group_new ("core");
+       gtk_action_group_set_translation_domain (
+               action_group, GETTEXT_PACKAGE);
+       gtk_action_group_add_actions (
+               action_group, core_entries,
+               G_N_ELEMENTS (core_entries), comp_editor);
+       gtk_ui_manager_insert_action_group (
+               comp_editor->priv->ui_manager, action_group, 0);
+       g_object_unref (action_group);
+
+       action_group = gtk_action_group_new ("editable");
+       gtk_action_group_set_translation_domain (
+               action_group, GETTEXT_PACKAGE);
+       gtk_action_group_add_actions (
+               action_group, editable_entries,
+               G_N_ELEMENTS (editable_entries), comp_editor);
+       gtk_ui_manager_insert_action_group (
+               comp_editor->priv->ui_manager, action_group, 0);
+       g_object_unref (action_group);
+
+       action = gtk_action_group_get_action (action_group, "save-and-close");
+       if (action) {
+               GtkAction *save_action;
+               GIcon *icon;
+               GIcon *emblemed_icon;
+               GEmblem *emblem;
+
+               icon = g_themed_icon_new ("window-close");
+               emblemed_icon = g_themed_icon_new ("document-save");
+               emblem = g_emblem_new (emblemed_icon);
+               g_object_unref (emblemed_icon);
+
+               emblemed_icon = g_emblemed_icon_new (icon, emblem);
+               g_object_unref (emblem);
+               g_object_unref (icon);
+
+               gtk_action_set_gicon (action, emblemed_icon);
+
+               g_object_unref (emblemed_icon);
+
+               save_action = gtk_action_group_get_action (action_group, "save");
+               e_binding_bind_property (
+                       save_action, "sensitive",
+                       action, "sensitive",
+                       G_BINDING_SYNC_CREATE);
+       }
+
+       gtk_ui_manager_add_ui_from_string (comp_editor->priv->ui_manager, ui, -1, &error);
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_widget_show (widget);
+
+       vbox = GTK_BOX (widget);
+
+       gtk_container_add (GTK_CONTAINER (comp_editor), widget);
+
+       widget = e_comp_editor_get_managed_widget (comp_editor, "/main-menu");
+       gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+       gtk_widget_set_visible (widget, TRUE);
+
+       widget = e_comp_editor_get_managed_widget (comp_editor, "/main-toolbar");
+       gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       gtk_style_context_add_class (
+               gtk_widget_get_style_context (widget),
+               GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
+
+       widget = e_alert_bar_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       comp_editor->priv->alert_bar = E_ALERT_BAR (widget);
+
+       gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
+
+       widget = e_activity_bar_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", FALSE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               NULL);
+
+       comp_editor->priv->activity_bar = E_ACTIVITY_BAR (widget);
+
+       gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
+
+       widget = gtk_notebook_new ();
+       g_object_set (G_OBJECT (widget),
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "show-tabs", TRUE,
+               "show-border", FALSE,
+               NULL);
+       gtk_widget_show (widget);
+
+       comp_editor->priv->content = GTK_NOTEBOOK (widget);
+
+       gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0);
+
+       /* Configure an EFocusTracker to manage selection actions. */
+
+       focus_tracker = e_focus_tracker_new (GTK_WINDOW (comp_editor));
+
+       action = e_comp_editor_get_action (comp_editor, "cut-clipboard");
+       e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
+
+       action = e_comp_editor_get_action (comp_editor, "copy-clipboard");
+       e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
+
+       action = e_comp_editor_get_action (comp_editor, "paste-clipboard");
+       e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
+
+       action = e_comp_editor_get_action (comp_editor, "delete-selection");
+       e_focus_tracker_set_delete_selection_action (focus_tracker, action);
+
+       action = e_comp_editor_get_action (comp_editor, "select-all");
+       e_focus_tracker_set_select_all_action (focus_tracker, action);
+
+       action = e_comp_editor_get_action (comp_editor, "undo");
+       e_focus_tracker_set_undo_action (focus_tracker, action);
+
+       action = e_comp_editor_get_action (comp_editor, "redo");
+       e_focus_tracker_set_redo_action (focus_tracker, action);
+
+       comp_editor->priv->focus_tracker = focus_tracker;
+
+       /* Desensitize the "save" action. */
+       action = e_comp_editor_get_action (comp_editor, "save");
+       gtk_action_set_sensitive (action, FALSE);
+
+       e_binding_bind_property (comp_editor, "changed", action, "sensitive", 0);
+
+       g_signal_connect (comp_editor, "realize", G_CALLBACK (comp_editor_realize_cb), NULL);
+       g_signal_connect (comp_editor, "unrealize", G_CALLBACK (comp_editor_unrealize_cb), NULL);
+
+       gtk_application_add_window (GTK_APPLICATION (comp_editor->priv->shell), GTK_WINDOW (comp_editor));
+}
+
+static void
+e_comp_editor_dispose (GObject *object)
+{
+       ECompEditor *comp_editor = E_COMP_EDITOR (object);
+
+       if (comp_editor->priv->page_general) {
+               g_signal_handlers_disconnect_by_func (comp_editor->priv->page_general,
+                       G_CALLBACK (comp_editor_selected_source_notify_cb), comp_editor);
+               comp_editor->priv->page_general = NULL;
+       }
+
+       if (comp_editor->priv->target_client_opening) {
+               e_activity_cancel (comp_editor->priv->target_client_opening);
+               g_clear_object (&comp_editor->priv->target_client_opening);
+       }
+
+       g_slist_free_full (comp_editor->priv->pages, g_object_unref);
+       comp_editor->priv->pages = NULL;
+
+       g_free (comp_editor->priv->alarm_email_address);
+       comp_editor->priv->alarm_email_address = NULL;
+
+       g_free (comp_editor->priv->cal_email_address);
+       comp_editor->priv->cal_email_address = NULL;
+
+       g_free (comp_editor->priv->title_suffix);
+       comp_editor->priv->title_suffix = NULL;
+
+       if (comp_editor->priv->component) {
+               icalcomponent_free (comp_editor->priv->component);
+               comp_editor->priv->component = NULL;
+       }
+
+       ece_connect_time_parts (comp_editor, NULL, NULL);
+
+       g_clear_object (&comp_editor->priv->origin_source);
+       g_clear_object (&comp_editor->priv->shell);
+       g_clear_object (&comp_editor->priv->focus_tracker);
+       g_clear_object (&comp_editor->priv->ui_manager);
+       g_clear_object (&comp_editor->priv->source_client);
+       g_clear_object (&comp_editor->priv->target_client);
+       g_clear_object (&comp_editor->priv->calendar_settings);
+       g_clear_object (&comp_editor->priv->validation_alert);
+
+       comp_editor->priv->activity_bar = NULL;
+
+       opened_editors = g_slist_remove (opened_editors, comp_editor);
+
+       G_OBJECT_CLASS (e_comp_editor_parent_class)->dispose (object);
+}
+
+static void
+e_comp_editor_init (ECompEditor *comp_editor)
+{
+       comp_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (comp_editor, E_TYPE_COMP_EDITOR, ECompEditorPrivate);
+}
+
+static void
+e_comp_editor_alert_sink_iface_init (EAlertSinkInterface *iface)
+{
+       iface->submit_alert = e_comp_editor_submit_alert;
+}
+
+static void
+e_comp_editor_class_init (ECompEditorClass *klass)
+{
+       GtkWidgetClass *widget_class;
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ECompEditorPrivate));
+
+       klass->sensitize_widgets = ece_sensitize_widgets;
+       klass->fill_widgets = ece_fill_widgets;
+       klass->fill_component = ece_fill_component;
+
+       widget_class = GTK_WIDGET_CLASS (klass);
+       widget_class->delete_event = comp_editor_delete_event;
+       widget_class->key_press_event = comp_editor_key_press_event;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = e_comp_editor_set_property;
+       object_class->get_property = e_comp_editor_get_property;
+       object_class->constructed = e_comp_editor_constructed;
+       object_class->dispose = e_comp_editor_dispose;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ALARM_EMAIL_ADDRESS,
+               g_param_spec_string (
+                       "alarm-email-address",
+                       "Alarm Email Address",
+                       "Target client's alarm email address",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CAL_EMAIL_ADDRESS,
+               g_param_spec_string (
+                       "cal-email-address",
+                       "Calendar Email Address",
+                       "Target client's calendar email address",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CHANGED,
+               g_param_spec_boolean (
+                       "changed",
+                       "Changed",
+                       "Whether the editor content changed",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_COMPONENT,
+               g_param_spec_pointer (
+                       "component",
+                       "Component",
+                       "icalcomponent currently edited",
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_FLAGS,
+               g_param_spec_uint (
+                       "flags",
+                       "Flags",
+                       "Editor flags",
+                       0, G_MAXUINT, 0,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ORIGIN_SOURCE,
+               g_param_spec_object (
+                       "origin-source",
+                       "Origin Source",
+                       "ESource of an ECalClient the component is stored in",
+                       E_TYPE_SOURCE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SHELL,
+               g_param_spec_object (
+                       "shell",
+                       "Shell",
+                       "EShell",
+                       E_TYPE_SHELL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_SOURCE_CLIENT,
+               g_param_spec_object (
+                       "source-client",
+                       "Source Client",
+                       "ECalClient, the source calendar for the component",
+                       E_TYPE_CAL_CLIENT,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_TARGET_CLIENT,
+               g_param_spec_object (
+                       "target-client",
+                       "Target Client",
+                       "ECalClient currently set as the target calendar for the component",
+                       E_TYPE_CAL_CLIENT,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_TITLE_SUFFIX,
+               g_param_spec_string (
+                       "title-suffix",
+                       "Title Suffix",
+                       "Window title suffix, usually summary of the component",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       signals[TIMES_CHANGED] = g_signal_new (
+               "times-changed",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (ECompEditorClass, times_changed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       signals[OBJECT_CREATED] = g_signal_new (
+               "object-created",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (ECompEditorClass, object_created),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       signals[EDITOR_CLOSED] = g_signal_new (
+               "editor-closed",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (ECompEditorClass, editor_closed),
+               NULL, NULL,
+               g_cclosure_marshal_VOID__BOOLEAN,
+               G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+void
+e_comp_editor_sensitize_widgets (ECompEditor *comp_editor)
+{
+       ECompEditorClass *comp_editor_class;
+       gboolean force_insensitive;
+       GtkWidget *current_focus;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
+       g_return_if_fail (comp_editor_class != NULL);
+       g_return_if_fail (comp_editor_class->sensitize_widgets != NULL);
+
+       current_focus = gtk_window_get_focus (GTK_WINDOW (comp_editor));
+
+       force_insensitive = !comp_editor->priv->component;
+
+       if (!force_insensitive) {
+               ECalClient *target_client;
+
+               target_client = e_comp_editor_get_target_client (comp_editor);
+               if (target_client) {
+                       EClient *client = E_CLIENT (target_client);
+
+                       if (e_client_is_readonly (client)) {
+                               force_insensitive = TRUE;
+                       } else {
+                               if (!e_cal_util_component_has_organizer (comp_editor->priv->component) ||
+                                   ece_organizer_is_user (comp_editor, comp_editor->priv->component, client) 
||
+                                   ece_sentby_is_user (comp_editor, comp_editor->priv->component, client)) {
+                                       comp_editor->priv->flags = comp_editor->priv->flags | 
E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
+                               } else {
+                                       comp_editor->priv->flags = comp_editor->priv->flags & 
(~E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER);
+                               }
+                       }
+               } else {
+                       force_insensitive = TRUE;
+               }
+       }
+
+       comp_editor_class->sensitize_widgets (comp_editor, force_insensitive);
+
+       if (force_insensitive)
+               comp_editor->priv->restore_focus = current_focus;
+       else
+               ece_restore_focus (comp_editor);
+}
+
+void
+e_comp_editor_fill_widgets (ECompEditor *comp_editor,
+                           icalcomponent *component)
+{
+       ECompEditorClass *comp_editor_class;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (component != NULL);
+
+       comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
+       g_return_if_fail (comp_editor_class != NULL);
+       g_return_if_fail (comp_editor_class->fill_widgets != NULL);
+
+       e_comp_editor_set_updating (comp_editor, TRUE);
+
+       comp_editor_class->fill_widgets (comp_editor, component);
+
+       e_comp_editor_set_updating (comp_editor, FALSE);
+}
+
+gboolean
+e_comp_editor_fill_component (ECompEditor *comp_editor,
+                             icalcomponent *component)
+{
+       ECompEditorClass *comp_editor_class;
+       gboolean is_valid;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor);
+       g_return_val_if_fail (comp_editor_class != NULL, FALSE);
+       g_return_val_if_fail (comp_editor_class->fill_component != NULL, FALSE);
+
+       is_valid = comp_editor_class->fill_component (comp_editor, component);
+
+       if (is_valid && comp_editor->priv->validation_alert) {
+               e_alert_response (comp_editor->priv->validation_alert, GTK_RESPONSE_CLOSE);
+               g_clear_object (&comp_editor->priv->validation_alert);
+       }
+
+       if (is_valid) {
+               ECalClient *target_client;
+               EClient *client = NULL;
+
+               target_client = e_comp_editor_get_target_client (comp_editor);
+               if (target_client)
+                       client = E_CLIENT (target_client);
+
+               if (!e_cal_util_component_has_organizer (component) || (client && (
+                   ece_organizer_is_user (comp_editor, component, client) ||
+                   ece_sentby_is_user (comp_editor, component, client)))) {
+                       gint sequence;
+
+                       sequence = icalcomponent_get_sequence (component);
+                       icalcomponent_set_sequence (component, sequence + 1);
+               }
+       }
+
+       return is_valid;
+}
+
+void
+e_comp_editor_set_validation_error (ECompEditor *comp_editor,
+                                   ECompEditorPage *error_page,
+                                   GtkWidget *error_widget,
+                                   const gchar *error_message)
+{
+       EAlert *alert, *previous_alert;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (error_message != NULL);
+
+       /* Ignore validation errors when the inner editor is currently updating. */
+       if (e_comp_editor_get_updating (comp_editor))
+               return;
+
+       alert = e_alert_new ("calendar:comp-editor-failed-validate", error_message, NULL);
+
+       e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert);
+
+       previous_alert = comp_editor->priv->validation_alert;
+       comp_editor->priv->validation_alert = alert;
+
+       if (previous_alert) {
+               e_alert_response (previous_alert, GTK_RESPONSE_CLOSE);
+               g_clear_object (&previous_alert);
+       }
+
+       if (error_page)
+               e_comp_editor_select_page (comp_editor, error_page);
+
+       if (error_widget)
+               gtk_widget_grab_focus (error_widget);
+}
+
+EShell *
+e_comp_editor_get_shell (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->shell;
+}
+
+GSettings *
+e_comp_editor_get_settings (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->calendar_settings;
+}
+
+ESource *
+e_comp_editor_get_origin_source (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->origin_source;
+}
+
+const icalcomponent *
+e_comp_editor_get_component (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->component;
+}
+
+guint32
+e_comp_editor_get_flags (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), 0);
+
+       return comp_editor->priv->flags;
+}
+
+EFocusTracker *
+e_comp_editor_get_focus_tracker (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->focus_tracker;
+}
+
+GtkUIManager *
+e_comp_editor_get_ui_manager (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->ui_manager;
+}
+
+GtkAction *
+e_comp_editor_get_action (ECompEditor *comp_editor,
+                         const gchar *action_name)
+{
+       GtkUIManager *ui_manager;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+       g_return_val_if_fail (action_name != NULL, NULL);
+
+       ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+
+       return e_lookup_action (ui_manager, action_name);
+}
+
+GtkActionGroup *
+e_comp_editor_get_action_group (ECompEditor *comp_editor,
+                               const gchar *group_name)
+{
+       GtkUIManager *ui_manager;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+       g_return_val_if_fail (group_name != NULL, NULL);
+
+       ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+
+       return e_lookup_action_group (ui_manager, group_name);
+}
+
+GtkWidget *
+e_comp_editor_get_managed_widget (ECompEditor *comp_editor,
+                                 const gchar *widget_path)
+{
+       GtkUIManager *ui_manager;
+       GtkWidget *widget;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+       g_return_val_if_fail (widget_path != NULL, NULL);
+
+       ui_manager = e_comp_editor_get_ui_manager (comp_editor);
+       widget = gtk_ui_manager_get_widget (ui_manager, widget_path);
+       g_return_val_if_fail (widget != NULL, NULL);
+
+       return widget;
+}
+
+const gchar *
+e_comp_editor_get_alarm_email_address (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->alarm_email_address;
+}
+
+void
+e_comp_editor_set_alarm_email_address (ECompEditor *comp_editor,
+                                      const gchar *alarm_email_address)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (g_strcmp0 (alarm_email_address, comp_editor->priv->alarm_email_address) == 0)
+               return;
+
+       g_free (comp_editor->priv->alarm_email_address);
+       comp_editor->priv->alarm_email_address = g_strdup (alarm_email_address);
+
+       g_object_notify (G_OBJECT (comp_editor), "alarm-email-address");
+}
+
+const gchar *
+e_comp_editor_get_cal_email_address (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->cal_email_address;
+}
+
+void
+e_comp_editor_set_cal_email_address (ECompEditor *comp_editor,
+                                    const gchar *cal_email_address)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (g_strcmp0 (cal_email_address, comp_editor->priv->cal_email_address) == 0)
+               return;
+
+       g_free (comp_editor->priv->cal_email_address);
+       comp_editor->priv->cal_email_address = g_strdup (cal_email_address);
+
+       g_object_notify (G_OBJECT (comp_editor), "cal-email-address");
+}
+
+gboolean
+e_comp_editor_get_changed (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+
+       return comp_editor->priv->changed;
+}
+
+void
+e_comp_editor_set_changed (ECompEditor *comp_editor,
+                          gboolean changed)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if ((changed ? 1 : 0) == (comp_editor->priv->changed ? 1 : 0))
+               return;
+
+       comp_editor->priv->changed = changed;
+
+       g_object_notify (G_OBJECT (comp_editor), "changed");
+}
+
+void
+e_comp_editor_ensure_changed (ECompEditor *comp_editor)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       e_comp_editor_set_changed (comp_editor, TRUE);
+}
+
+gboolean
+e_comp_editor_get_updating (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+
+       return comp_editor->priv->updating > 0;
+}
+
+void
+e_comp_editor_set_updating (ECompEditor *comp_editor,
+                           gboolean updating)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (updating) {
+               comp_editor->priv->updating++;
+       } else if (comp_editor->priv->updating > 0) {
+               comp_editor->priv->updating--;
+       } else {
+               g_warn_if_reached ();
+       }
+}
+
+ECalClient *
+e_comp_editor_get_source_client (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->source_client;
+}
+
+void
+e_comp_editor_set_source_client (ECompEditor *comp_editor,
+                                ECalClient *client)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (client == comp_editor->priv->source_client)
+               return;
+
+       if (client)
+               g_object_ref (client);
+       g_clear_object (&comp_editor->priv->source_client);
+       comp_editor->priv->source_client = client;
+
+       g_object_notify (G_OBJECT (comp_editor), "source-client");
+}
+
+ECalClient *
+e_comp_editor_get_target_client (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->target_client;
+}
+
+void
+e_comp_editor_set_target_client (ECompEditor *comp_editor,
+                                ECalClient *client)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (client == comp_editor->priv->target_client)
+               return;
+
+       if (client)
+               g_object_ref (client);
+       g_clear_object (&comp_editor->priv->target_client);
+       comp_editor->priv->target_client = client;
+
+       g_object_notify (G_OBJECT (comp_editor), "target-client");
+
+       if (client && !comp_editor->priv->source_client && comp_editor->priv->origin_source &&
+           e_source_equal (e_client_get_source (E_CLIENT (client)), comp_editor->priv->origin_source))
+               e_comp_editor_set_source_client (comp_editor, client);
+
+       e_comp_editor_sensitize_widgets (comp_editor);
+}
+
+const gchar *
+e_comp_editor_get_title_suffix (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return comp_editor->priv->title_suffix;
+}
+
+void
+e_comp_editor_set_title_suffix (ECompEditor *comp_editor,
+                               const gchar *title_suffix)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (g_strcmp0 (title_suffix, comp_editor->priv->title_suffix) == 0)
+               return;
+
+       g_free (comp_editor->priv->title_suffix);
+       comp_editor->priv->title_suffix = g_strdup (title_suffix);
+
+       g_object_notify (G_OBJECT (comp_editor), "title-suffix");
+
+       e_comp_editor_update_window_title (comp_editor);
+}
+
+void
+e_comp_editor_set_time_parts (ECompEditor *comp_editor,
+                             ECompEditorPropertyPart *dtstart_part,
+                             ECompEditorPropertyPart *dtend_part)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (dtstart_part)
+               g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part));
+       if (dtend_part)
+               g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part));
+
+       ece_connect_time_parts (comp_editor, dtstart_part, dtend_part);
+}
+
+void
+e_comp_editor_get_time_parts (ECompEditor *comp_editor,
+                             ECompEditorPropertyPart **out_dtstart_part,
+                             ECompEditorPropertyPart **out_dtend_part)
+{
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+
+       if (out_dtstart_part)
+               *out_dtstart_part = comp_editor->priv->dtstart_part;
+       if (out_dtend_part)
+               *out_dtend_part = comp_editor->priv->dtend_part;
+}
+
+/* This consumes the @page. */
+void
+e_comp_editor_add_page (ECompEditor *comp_editor,
+                       const gchar *label,
+                       ECompEditorPage *page)
+{
+       ECompEditor *pages_comp_editor;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (label != NULL);
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       pages_comp_editor = e_comp_editor_page_ref_editor (page);
+       if (pages_comp_editor != comp_editor) {
+               g_warn_if_fail (pages_comp_editor == comp_editor);
+               g_clear_object (&pages_comp_editor);
+               return;
+       }
+
+       g_clear_object (&pages_comp_editor);
+
+       /* One reference uses the GtkNotebook, the other the pages GSList */
+       gtk_notebook_append_page (comp_editor->priv->content,
+               GTK_WIDGET (page),
+               gtk_label_new_with_mnemonic (label));
+
+       comp_editor->priv->pages = g_slist_append (comp_editor->priv->pages, g_object_ref (page));
+
+       g_signal_connect_swapped (page, "changed", G_CALLBACK (e_comp_editor_ensure_changed), comp_editor);
+
+       if (E_IS_COMP_EDITOR_PAGE_GENERAL (page)) {
+               ECompEditorPageGeneral *page_general;
+
+               g_return_if_fail (comp_editor->priv->page_general == NULL);
+
+               page_general = E_COMP_EDITOR_PAGE_GENERAL (page);
+
+               g_signal_connect (page_general, "notify::selected-source",
+                       G_CALLBACK (comp_editor_selected_source_notify_cb), comp_editor);
+
+               comp_editor->priv->page_general = page_general;
+
+               if ((comp_editor->priv->flags & E_COMP_EDITOR_FLAG_WITH_ATTENDEES) != 0) {
+                       e_comp_editor_page_general_set_show_attendees (page_general, TRUE);
+               }
+       }
+}
+
+/* The returned pointer is owned by the @comp_editor; returns the first instance,
+   in order of the addition. */
+ECompEditorPage *
+e_comp_editor_get_page (ECompEditor *comp_editor,
+                       GType page_type)
+{
+       GSList *link;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+       g_return_val_if_fail (g_type_is_a (page_type, E_TYPE_COMP_EDITOR_PAGE), NULL);
+       g_return_val_if_fail (page_type != E_TYPE_COMP_EDITOR_PAGE, NULL);
+
+       for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) {
+               ECompEditorPage *page = link->data;
+
+               if (G_TYPE_CHECK_INSTANCE_TYPE (page, page_type))
+                       return page;
+       }
+
+       return NULL;
+}
+
+/* Free the returned GSList with g_slist_free(), the memebers are owned by the comp_editor */
+GSList *
+e_comp_editor_get_pages (ECompEditor *comp_editor)
+{
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+
+       return g_slist_copy (comp_editor->priv->pages);
+}
+
+void
+e_comp_editor_select_page (ECompEditor *comp_editor,
+                          ECompEditorPage *page)
+{
+       gint page_num;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page));
+
+       page_num = gtk_notebook_page_num (comp_editor->priv->content, GTK_WIDGET (page));
+       g_return_if_fail (page_num != -1);
+
+       gtk_notebook_set_current_page (comp_editor->priv->content, page_num);
+}
+
+/* Unref returned pointer when done with it. */
+static EAlert *
+e_comp_editor_add_alert (ECompEditor *comp_editor,
+                        const gchar *alert_id,
+                        const gchar *primary_text,
+                        const gchar *secondary_text)
+{
+       EAlert *alert;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL);
+       g_return_val_if_fail (alert_id != NULL, NULL);
+       g_return_val_if_fail (primary_text != NULL || secondary_text != NULL, NULL);
+
+       alert = e_alert_new (alert_id,
+               primary_text ? primary_text : "",
+               secondary_text ? secondary_text : "",
+               NULL);
+
+       e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert);
+
+       return alert;
+}
+
+/* Unref returned pointer when done with it. */
+EAlert *
+e_comp_editor_add_information (ECompEditor *comp_editor,
+                              const gchar *primary_text,
+                              const gchar *secondary_text)
+{
+       return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-information", primary_text, 
secondary_text);
+}
+
+/* Unref returned pointer when done with it. */
+EAlert *
+e_comp_editor_add_warning (ECompEditor *comp_editor,
+                          const gchar *primary_text,
+                          const gchar *secondary_text)
+{
+       return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-warning", primary_text, 
secondary_text);
+}
+
+/* Unref returned pointer when done with it. */
+EAlert *
+e_comp_editor_add_error (ECompEditor *comp_editor,
+                        const gchar *primary_text,
+                        const gchar *secondary_text)
+{
+       return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-error", primary_text, 
secondary_text);
+}
+
+
+static gboolean
+ece_check_start_before_end (struct icaltimetype *start_tt,
+                           struct icaltimetype *end_tt,
+                           gboolean adjust_end_time)
+{
+       struct icaltimetype end_tt_copy;
+       icaltimezone *start_zone, *end_zone;
+       gint cmp;
+
+       start_zone = (icaltimezone *) start_tt->zone;
+       end_zone = (icaltimezone *) end_tt->zone;
+
+       /* Convert the end time to the same timezone as the start time. */
+       end_tt_copy = *end_tt;
+
+       if (start_zone && end_zone && start_zone != end_zone)
+               icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone);
+
+       /* Now check if the start time is after the end time. If it is,
+        * we need to modify one of the times. */
+       cmp = icaltime_compare (*start_tt, end_tt_copy);
+       if (cmp > 0) {
+               if (adjust_end_time) {
+                       /* Try to switch only the date */
+                       end_tt->year = start_tt->year;
+                       end_tt->month = start_tt->month;
+                       end_tt->day = start_tt->day;
+
+                       end_tt_copy = *end_tt;
+                       if (start_zone && end_zone && start_zone != end_zone)
+                               icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone);
+
+                       if (icaltime_compare (*start_tt, end_tt_copy) >= 0) {
+                               /* Modify the end time, to be the start + 1 hour/day. */
+                               *end_tt = *start_tt;
+                               icaltime_adjust (end_tt, 0, start_tt->is_date ? 24 : 1, 0, 0);
+
+                               if (start_zone && end_zone && start_zone != end_zone)
+                                       icaltimezone_convert_time (end_tt, start_zone, end_zone);
+                       }
+               } else {
+                       /* Try to switch only the date */
+                       start_tt->year = end_tt->year;
+                       start_tt->month = end_tt->month;
+                       start_tt->day = end_tt->day;
+
+                       if (icaltime_compare (*start_tt, end_tt_copy) >= 0) {
+                               /* Modify the start time, to be the end - 1 hour/day. */
+                               *start_tt = *end_tt;
+                               icaltime_adjust (start_tt, 0, start_tt->is_date ? -24 : -1, 0, 0);
+
+                               if (start_zone && end_zone && start_zone != end_zone)
+                                       icaltimezone_convert_time (start_tt, end_zone, start_zone);
+                       }
+               }
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+void
+e_comp_editor_ensure_start_before_end (ECompEditor *comp_editor,
+                                      ECompEditorPropertyPart *start_datetime,
+                                      ECompEditorPropertyPart *end_datetime,
+                                      gboolean change_end_datetime)
+{
+       ECompEditorPropertyPartDatetime *start_dtm, *end_dtm;
+       struct icaltimetype start_tt, end_tt;
+       gboolean set_dtstart = FALSE, set_dtend = FALSE;
+
+       g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (start_datetime));
+       g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (end_datetime));
+
+       start_dtm = E_COMP_EDITOR_PROPERTY_PART_DATETIME (start_datetime);
+       end_dtm = E_COMP_EDITOR_PROPERTY_PART_DATETIME (end_datetime);
+
+       start_tt = e_comp_editor_property_part_datetime_get_value (start_dtm);
+       end_tt = e_comp_editor_property_part_datetime_get_value (end_dtm);
+
+       if (icaltime_is_null_time (start_tt) ||
+           icaltime_is_null_time (end_tt) ||
+           !icaltime_is_valid_time (start_tt) ||
+           !icaltime_is_valid_time (end_tt))
+               return;
+
+       if (start_tt.is_date || end_tt.is_date) {
+               /* All Day Events are simple. We just compare the dates and if
+                * start > end we copy one of them to the other. */
+               gint cmp;
+
+               start_tt.is_date = TRUE;
+               end_tt.is_date = TRUE;
+
+               cmp = icaltime_compare_date_only (start_tt, end_tt);
+
+               if (cmp > 0) {
+                       if (change_end_datetime) {
+                               end_tt = start_tt;
+                               set_dtend = TRUE;
+                       } else {
+                               start_tt = end_tt;
+                               set_dtstart = TRUE;
+                       }
+               }
+       } else {
+               if (ece_check_start_before_end (&start_tt, &end_tt, change_end_datetime)) {
+                       if (change_end_datetime)
+                               set_dtend = TRUE;
+                       else
+                               set_dtstart = TRUE;
+               }
+       }
+
+       if (set_dtstart || set_dtend) {
+               e_comp_editor_set_updating (comp_editor, TRUE);
+
+               if (set_dtstart)
+                       e_comp_editor_property_part_datetime_set_value (start_dtm, start_tt);
+
+               if (set_dtend)
+                       e_comp_editor_property_part_datetime_set_value (end_dtm, end_tt);
+
+               e_comp_editor_set_updating (comp_editor, FALSE);
+       }
+}
+
+static gboolean
+e_comp_editor_holds_component (ECompEditor *comp_editor,
+                              ESource *origin_source,
+                              const icalcomponent *component)
+{
+       const gchar *component_uid, *editor_uid;
+       gboolean equal;
+
+       g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE);
+       g_return_val_if_fail (component != NULL, FALSE);
+
+       if (!origin_source || !comp_editor->priv->origin_source ||
+           !e_source_equal (origin_source, comp_editor->priv->origin_source))
+               return FALSE;
+
+       component_uid = icalcomponent_get_uid ((icalcomponent *) component);
+       editor_uid = icalcomponent_get_uid (comp_editor->priv->component);
+
+       if (!component_uid || !editor_uid)
+               return FALSE;
+
+       equal = g_strcmp0 (component_uid, editor_uid) == 0;
+       if (equal) {
+               struct icaltimetype component_rid, editor_rid;
+
+               component_rid = icalcomponent_get_recurrenceid ((icalcomponent *) component);
+               editor_rid = icalcomponent_get_recurrenceid (comp_editor->priv->component);
+
+               if (icaltime_is_null_time (component_rid)) {
+                       equal = icaltime_is_null_time (editor_rid);
+               } else if (!icaltime_is_null_time (editor_rid)) {
+                       equal = icaltime_compare (component_rid, editor_rid) == 0;
+               }
+       }
+
+       return equal;
+}
+
+ECompEditor *
+e_comp_editor_open_for_component (GtkWindow *parent,
+                                 EShell *shell,
+                                 ESource *origin_source,
+                                 const icalcomponent *component,
+                                 guint32 flags /* bit-or of ECompEditorFlags */)
+{
+       ECompEditor *comp_editor;
+       GType comp_editor_type;
+
+       g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+       if (origin_source)
+               g_return_val_if_fail (E_IS_SOURCE (origin_source), NULL);
+       g_return_val_if_fail (component != NULL, NULL);
+
+       comp_editor = e_comp_editor_find_existing_for (origin_source, component);
+       if (comp_editor) {
+               gtk_window_present (GTK_WINDOW (comp_editor));
+               return comp_editor;
+       }
+
+       switch (icalcomponent_isa (component)) {
+               case ICAL_VEVENT_COMPONENT:
+                       comp_editor_type = E_TYPE_COMP_EDITOR_EVENT;
+                       break;
+               case ICAL_VTODO_COMPONENT:
+                       comp_editor_type = E_TYPE_COMP_EDITOR_TASK;
+                       break;
+               case ICAL_VJOURNAL_COMPONENT:
+                       comp_editor_type = E_TYPE_COMP_EDITOR_MEMO;
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return NULL;
+       }
+
+       comp_editor = g_object_new (comp_editor_type,
+               "shell", shell,
+               "origin-source", origin_source,
+               "component", component,
+               "flags", flags,
+               NULL);
+
+       opened_editors = g_slist_prepend (opened_editors, comp_editor);
+
+       gtk_widget_show (GTK_WIDGET (comp_editor));
+
+       return comp_editor;
+}
+
+ECompEditor *
+e_comp_editor_find_existing_for (ESource *origin_source,
+                                const icalcomponent *component)
+{
+       ECompEditor *comp_editor;
+       GSList *link;
+
+       if (origin_source)
+               g_return_val_if_fail (E_IS_SOURCE (origin_source), NULL);
+       g_return_val_if_fail (component != NULL, NULL);
+
+       for (link = opened_editors; link; link = g_slist_next (link)) {
+               comp_editor = link->data;
+
+               if (!comp_editor)
+                       continue;
+
+               if (e_comp_editor_holds_component (comp_editor, origin_source, component)) {
+                       gtk_window_present (GTK_WINDOW (comp_editor));
+                       return comp_editor;
+               }
+       }
+
+       return NULL;
+}
diff --git a/calendar/gui/e-comp-editor.h b/calendar/gui/e-comp-editor.h
new file mode 100644
index 0000000..6b5dc2f
--- /dev/null
+++ b/calendar/gui/e-comp-editor.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_COMP_EDITOR_H
+#define E_COMP_EDITOR_H
+
+#include <gtk/gtk.h>
+#include <libecal/libecal.h>
+
+#include <e-util/e-util.h>
+#include <shell/e-shell.h>
+
+#include <calendar/gui/e-comp-editor-page.h>
+#include <calendar/gui/e-comp-editor-property-part.h>
+
+/* Standard GObject macros */
+
+#define E_TYPE_COMP_EDITOR \
+       (e_comp_editor_get_type ())
+#define E_COMP_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_COMP_EDITOR, ECompEditor))
+#define E_COMP_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_COMP_EDITOR, ECompEditorClass))
+#define E_IS_COMP_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_COMP_EDITOR))
+#define E_IS_COMP_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_COMP_EDITOR))
+#define E_COMP_EDITOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_COMP_EDITOR, ECompEditorClass))
+
+G_BEGIN_DECLS
+
+typedef enum {
+       E_COMP_EDITOR_FLAG_IS_NEW               = 1 << 0,
+       E_COMP_EDITOR_FLAG_IS_ALL_DAY_EVENT     = 1 << 1,
+       E_COMP_EDITOR_FLAG_WITH_ATTENDEES       = 1 << 2,
+       E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER    = 1 << 3,
+       E_COMP_EDITOR_FLAG_DELEGATE             = 1 << 4
+} ECompEditorFlags;
+
+typedef struct _ECompEditor ECompEditor;
+typedef struct _ECompEditorClass ECompEditorClass;
+typedef struct _ECompEditorPrivate ECompEditorPrivate;
+
+struct _ECompEditor {
+       GtkWindow parent;
+
+       ECompEditorPrivate *priv;
+};
+
+struct _ECompEditorClass {
+       GtkWindowClass parent_class;
+
+       const gchar *help_section;
+       const gchar *title_format_with_attendees; /* should contain only one '%s', for the component summary 
*/
+       const gchar *title_format_without_attendees; /* should contain only one '%s', for the component 
summary */
+       const gchar *icon_name; /* to be set as window icon */
+
+       /* Virtual functions */
+       void            (* sensitize_widgets)   (ECompEditor *comp_editor,
+                                                gboolean force_insensitive);
+       void            (* fill_widgets)        (ECompEditor *comp_editor,
+                                                icalcomponent *component);
+       gboolean        (* fill_component)      (ECompEditor *comp_editor,
+                                                icalcomponent *component);
+
+       /* Signals */
+       void            (* times_changed)       (ECompEditor *comp_editor);
+       void            (* object_created)      (ECompEditor *comp_editor);
+       void            (* editor_closed)       (ECompEditor *comp_editor,
+                                                gboolean saved);
+};
+
+GType          e_comp_editor_get_type          (void) G_GNUC_CONST;
+void           e_comp_editor_sensitize_widgets (ECompEditor *comp_editor);
+void           e_comp_editor_fill_widgets      (ECompEditor *comp_editor,
+                                                icalcomponent *component);
+gboolean       e_comp_editor_fill_component    (ECompEditor *comp_editor,
+                                                icalcomponent *component);
+void           e_comp_editor_set_validation_error
+                                               (ECompEditor *comp_editor,
+                                                ECompEditorPage *error_page,
+                                                GtkWidget *error_widget,
+                                                const gchar *error_message);
+EShell *       e_comp_editor_get_shell         (ECompEditor *comp_editor);
+GSettings *    e_comp_editor_get_settings      (ECompEditor *comp_editor);
+ESource *      e_comp_editor_get_origin_source (ECompEditor *comp_editor);
+const icalcomponent *
+               e_comp_editor_get_component     (ECompEditor *comp_editor);
+guint32                e_comp_editor_get_flags         (ECompEditor *comp_editor);
+EFocusTracker *        e_comp_editor_get_focus_tracker (ECompEditor *comp_editor);
+GtkUIManager * e_comp_editor_get_ui_manager    (ECompEditor *comp_editor);
+GtkAction *    e_comp_editor_get_action        (ECompEditor *comp_editor,
+                                                const gchar *action_name);
+GtkActionGroup *e_comp_editor_get_action_group (ECompEditor *comp_editor,
+                                                const gchar *group_name);
+GtkWidget *    e_comp_editor_get_managed_widget
+                                               (ECompEditor *comp_editor,
+                                                const gchar *widget_path);
+const gchar *  e_comp_editor_get_alarm_email_address
+                                               (ECompEditor *comp_editor);
+void           e_comp_editor_set_alarm_email_address
+                                               (ECompEditor *comp_editor,
+                                                const gchar *alarm_email_address);
+const gchar *  e_comp_editor_get_cal_email_address
+                                               (ECompEditor *comp_editor);
+void           e_comp_editor_set_cal_email_address
+                                               (ECompEditor *comp_editor,
+                                                const gchar *cal_email_address);
+gboolean       e_comp_editor_get_changed       (ECompEditor *comp_editor);
+void           e_comp_editor_set_changed       (ECompEditor *comp_editor,
+                                                gboolean changed);
+void           e_comp_editor_ensure_changed    (ECompEditor *comp_editor);
+gboolean       e_comp_editor_get_updating      (ECompEditor *comp_editor);
+void           e_comp_editor_set_updating      (ECompEditor *comp_editor,
+                                                gboolean updating);
+ECalClient *   e_comp_editor_get_source_client (ECompEditor *comp_editor);
+void           e_comp_editor_set_source_client (ECompEditor *comp_editor,
+                                                ECalClient *client);
+ECalClient *   e_comp_editor_get_target_client (ECompEditor *comp_editor);
+void           e_comp_editor_set_target_client (ECompEditor *comp_editor,
+                                                ECalClient *client);
+const gchar *  e_comp_editor_get_title_suffix  (ECompEditor *comp_editor);
+void           e_comp_editor_set_title_suffix  (ECompEditor *comp_editor,
+                                                const gchar *title_suffix);
+void           e_comp_editor_set_time_parts    (ECompEditor *comp_editor,
+                                                ECompEditorPropertyPart *dtstart_part,
+                                                ECompEditorPropertyPart *dtend_part);
+void           e_comp_editor_get_time_parts    (ECompEditor *comp_editor,
+                                                ECompEditorPropertyPart **out_dtstart_part,
+                                                ECompEditorPropertyPart **out_dtend_part);
+void           e_comp_editor_add_page          (ECompEditor *comp_editor,
+                                                const gchar *label,
+                                                ECompEditorPage *page);
+ECompEditorPage *
+               e_comp_editor_get_page          (ECompEditor *comp_editor,
+                                                GType page_type);
+GSList *       e_comp_editor_get_pages         (ECompEditor *comp_editor);
+void           e_comp_editor_select_page       (ECompEditor *comp_editor,
+                                                ECompEditorPage *page);
+EAlert *       e_comp_editor_add_information   (ECompEditor *comp_editor,
+                                                const gchar *primary_text,
+                                                const gchar *secondary_text);
+EAlert *       e_comp_editor_add_warning       (ECompEditor *comp_editor,
+                                                const gchar *primary_text,
+                                                const gchar *secondary_text);
+EAlert *       e_comp_editor_add_error         (ECompEditor *comp_editor,
+                                                const gchar *primary_text,
+                                                const gchar *secondary_text);
+void           e_comp_editor_ensure_start_before_end
+                                               (ECompEditor *comp_editor,
+                                                ECompEditorPropertyPart *start_datetime,
+                                                ECompEditorPropertyPart *end_datetime,
+                                                gboolean change_end_datetime);
+ECompEditor *  e_comp_editor_open_for_component
+                                               (GtkWindow *parent,
+                                                EShell *shell,
+                                                ESource *origin_source,
+                                                const icalcomponent *component,
+                                                guint32 flags /* bit-or of ECompEditorFlags */);
+ECompEditor *  e_comp_editor_find_existing_for (ESource *origin_source,
+                                                const icalcomponent *component);
+
+G_END_DECLS
+
+#endif /* E_COMP_EDITOR_H */
diff --git a/calendar/gui/e-date-time-list.c b/calendar/gui/e-date-time-list.c
index 16835c9..47f9ab5 100644
--- a/calendar/gui/e-date-time-list.c
+++ b/calendar/gui/e-date-time-list.c
@@ -66,34 +66,27 @@ G_DEFINE_TYPE_WITH_CODE (
                GTK_TYPE_TREE_MODEL, e_date_time_list_tree_model_init))
 
 static void
-free_datetime (ECalComponentDateTime *datetime)
+free_datetime (struct icaltimetype *itt)
 {
-       g_free (datetime->value);
-       if (datetime->tzid)
-               g_free ((gchar *) datetime->tzid);
-       g_free (datetime);
+       g_free (itt);
 }
 
-static ECalComponentDateTime *
-copy_datetime (const ECalComponentDateTime *datetime)
+static struct icaltimetype *
+copy_datetime (const struct icaltimetype itt)
 {
-       ECalComponentDateTime *datetime_copy;
+       struct icaltimetype *itt_copy;
 
-       datetime_copy = g_new0 (ECalComponentDateTime, 1);
-       datetime_copy->value = g_new (struct icaltimetype, 1);
-       *datetime_copy->value = *datetime->value;
+       itt_copy = g_new0 (struct icaltimetype, 1);
+       *itt_copy = itt;
 
-       if (datetime->tzid)
-               datetime_copy->tzid = g_strdup (datetime->tzid);
-
-       return datetime_copy;
+       return itt_copy;
 }
 
 static gint
-compare_datetime (const ECalComponentDateTime *datetime1,
-                  const ECalComponentDateTime *datetime2)
+compare_datetime (const struct icaltimetype *itt1,
+                  const struct icaltimetype *itt2)
 {
-       return icaltime_compare (*datetime1->value, *datetime2->value);
+       return icaltime_compare (*itt1, *itt2);
 }
 
 static void
@@ -164,7 +157,7 @@ row_updated (EDateTimeList *date_time_list,
 /* Builds a static string out of an exception date */
 static gchar *
 get_exception_string (EDateTimeList *date_time_list,
-                      ECalComponentDateTime *dt)
+                      struct icaltimetype *itt)
 {
        static gchar buf[256];
        struct icaltimetype tt;
@@ -175,12 +168,7 @@ get_exception_string (EDateTimeList *date_time_list,
        use_24_hour_format = e_date_time_list_get_use_24_hour_format (date_time_list);
        zone = e_date_time_list_get_timezone (date_time_list);
 
-       tt = *dt->value;
-       if (!tt.zone && dt->tzid) {
-               tt.zone = icaltimezone_get_builtin_timezone_from_tzid (dt->tzid);
-               if (!tt.zone)
-                       tt.zone = icaltimezone_get_builtin_timezone (dt->tzid);
-       }
+       tt = *itt;
 
        if (zone)
                tt = icaltime_convert_to_zone (tt, zone);
@@ -336,7 +324,7 @@ date_time_list_get_value (GtkTreeModel *tree_model,
                           GValue *value)
 {
        EDateTimeList        *date_time_list = E_DATE_TIME_LIST (tree_model);
-       ECalComponentDateTime *datetime;
+       struct icaltimetype  *itt;
        GList                *l;
        const gchar          *str;
 
@@ -351,14 +339,14 @@ date_time_list_get_value (GtkTreeModel *tree_model,
                return;
 
        l = iter->user_data;
-       datetime = l->data;
+       itt = l->data;
 
-       if (!datetime)
+       if (!itt)
                return;
 
        switch (column) {
                case E_DATE_TIME_LIST_COLUMN_DESCRIPTION:
-                       str = get_exception_string (date_time_list, datetime);
+                       str = get_exception_string (date_time_list, itt);
                        g_value_set_string (value, str);
                        break;
        }
@@ -533,7 +521,7 @@ e_date_time_list_new (void)
        return g_object_new (E_TYPE_DATE_TIME_LIST, NULL);
 }
 
-const ECalComponentDateTime *
+struct icaltimetype *
 e_date_time_list_get_date_time (EDateTimeList *date_time_list,
                                 GtkTreeIter *iter)
 {
@@ -545,18 +533,17 @@ e_date_time_list_get_date_time (EDateTimeList *date_time_list,
 void
 e_date_time_list_set_date_time (EDateTimeList *date_time_list,
                                 GtkTreeIter *iter,
-                                const ECalComponentDateTime *datetime)
+                                const struct icaltimetype itt)
 {
-       ECalComponentDateTime *datetime_old;
+       struct icaltimetype *itt_old;
 
        g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
 
-       datetime_old = G_LIST (iter->user_data)->data;
-       free_datetime (datetime_old);
-       G_LIST (iter->user_data)->data = copy_datetime (datetime);
-       row_updated (
-               date_time_list, g_list_position (
-               date_time_list->priv->list, G_LIST (iter->user_data)));
+       itt_old = G_LIST (iter->user_data)->data;
+       free_datetime (itt_old);
+       G_LIST (iter->user_data)->data = copy_datetime (itt);
+       row_updated (date_time_list,
+               g_list_position (date_time_list->priv->list, G_LIST (iter->user_data)));
 }
 
 gboolean
@@ -606,15 +593,12 @@ e_date_time_list_set_timezone (EDateTimeList *date_time_list,
 void
 e_date_time_list_append (EDateTimeList *date_time_list,
                          GtkTreeIter *iter,
-                         const ECalComponentDateTime *datetime)
+                         const struct icaltimetype itt)
 {
-       g_return_if_fail (datetime != NULL);
+       g_return_if_fail (icaltime_is_valid_time (itt));
 
-       if (g_list_find_custom (
-                       date_time_list->priv->list, datetime,
-                       (GCompareFunc) compare_datetime) == NULL) {
-               date_time_list->priv->list = g_list_append (
-                       date_time_list->priv->list, copy_datetime (datetime));
+       if (g_list_find_custom (date_time_list->priv->list, &itt, (GCompareFunc) compare_datetime) == NULL) {
+               date_time_list->priv->list = g_list_append (date_time_list->priv->list, copy_datetime (itt));
                row_added (date_time_list, g_list_length (date_time_list->priv->list) - 1);
        }
 
@@ -633,7 +617,7 @@ e_date_time_list_remove (EDateTimeList *date_time_list,
        g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
 
        n = g_list_position (date_time_list->priv->list, G_LIST (iter->user_data));
-       free_datetime ((ECalComponentDateTime *) G_LIST (iter->user_data)->data);
+       free_datetime (G_LIST (iter->user_data)->data);
        date_time_list->priv->list = g_list_delete_link (
                date_time_list->priv->list, G_LIST (iter->user_data));
        row_deleted (date_time_list, n);
@@ -647,7 +631,7 @@ e_date_time_list_clear (EDateTimeList *date_time_list)
        all_rows_deleted (date_time_list);
 
        for (l = date_time_list->priv->list; l; l = g_list_next (l)) {
-               free_datetime ((ECalComponentDateTime *) l->data);
+               free_datetime (l->data);
        }
 
        g_list_free (date_time_list->priv->list);
diff --git a/calendar/gui/e-date-time-list.h b/calendar/gui/e-date-time-list.h
index 4b21b50..d9178cd 100644
--- a/calendar/gui/e-date-time-list.h
+++ b/calendar/gui/e-date-time-list.h
@@ -70,12 +70,13 @@ struct _EDateTimeListClass {
 
 GType          e_date_time_list_get_type       (void);
 EDateTimeList *        e_date_time_list_new            (void);
-const ECalComponentDateTime *
-               e_date_time_list_get_date_time  (EDateTimeList *date_time_list,
+struct icaltimetype *
+               e_date_time_list_get_date_time
+                                               (EDateTimeList *date_time_list,
                                                 GtkTreeIter *iter);
 void           e_date_time_list_set_date_time  (EDateTimeList *date_time_list,
                                                 GtkTreeIter *iter,
-                                                const ECalComponentDateTime *datetime);
+                                                const struct icaltimetype itt);
 gboolean       e_date_time_list_get_use_24_hour_format
                                                (EDateTimeList *date_time_list);
 void           e_date_time_list_set_use_24_hour_format
@@ -86,7 +87,7 @@ void          e_date_time_list_set_timezone   (EDateTimeList *date_time_list,
                                                 icaltimezone *zone);
 void           e_date_time_list_append         (EDateTimeList *date_time_list,
                                                 GtkTreeIter *iter,
-                                                const ECalComponentDateTime *datetime);
+                                                const struct icaltimetype itt);
 void           e_date_time_list_remove         (EDateTimeList *date_time_list,
                                                 GtkTreeIter *iter);
 void           e_date_time_list_clear          (EDateTimeList *date_time_list);
diff --git a/calendar/gui/e-day-view.c b/calendar/gui/e-day-view.c
index 8c44bcd..9f020a2 100644
--- a/calendar/gui/e-day-view.c
+++ b/calendar/gui/e-day-view.c
@@ -35,12 +35,7 @@
 
 #include "libgnomecanvas/libgnomecanvas.h"
 
-#include "dialogs/delete-comp.h"
-#include "dialogs/send-comp.h"
-#include "dialogs/cancel-comp.h"
-#include "dialogs/recur-comp.h"
-#include "dialogs/goto-dialog.h"
-#include "dialogs/save-comp.h"
+#include "e-cal-dialogs.h"
 #include "e-util/e-util.h"
 
 #include "calendar-config.h"
@@ -5405,7 +5400,7 @@ e_day_view_finish_long_event_resize (EDayView *day_view)
 
        e_cal_component_commit_sequence (comp);
        if (e_cal_component_has_recurrences (comp)) {
-               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                        gtk_widget_queue_draw (day_view->top_canvas);
                        goto out;
                }
@@ -5500,7 +5495,7 @@ e_day_view_finish_resize (EDayView *day_view)
 
        if ((itip_organizer_is_user (registry, comp, client) ||
             itip_sentby_is_user (registry, comp, client)))
-               send = send_dragged_or_resized_component_dialog (
+               send = e_cal_dialogs_send_dragged_or_resized_component (
                                toplevel, client, comp, &strip_alarms, &only_new_attendees);
 
        if (send == GTK_RESPONSE_CANCEL) {
@@ -5539,7 +5534,7 @@ e_day_view_finish_resize (EDayView *day_view)
        day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
 
        if (e_cal_component_has_recurrences (comp)) {
-               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                        gtk_widget_queue_draw (day_view->main_canvas);
                        goto out;
                }
@@ -7627,7 +7622,7 @@ e_day_view_change_event_time (EDayView *day_view,
        day_view->resize_drag_pos = E_CALENDAR_VIEW_POS_NONE;
 
        if (e_cal_component_has_recurrences (comp)) {
-               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                        gtk_widget_queue_draw (day_view->top_canvas);
                        goto out;
                }
@@ -7886,7 +7881,7 @@ e_day_view_on_editing_stopped (EDayView *day_view,
                        ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
 
                        if (e_cal_component_has_recurrences (comp)) {
-                               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+                               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                                        goto out;
                                }
 
@@ -9121,7 +9116,7 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
 
                        if ((itip_organizer_is_user (registry, comp, client) ||
                             itip_sentby_is_user (registry, comp, client)))
-                               send = send_dragged_or_resized_component_dialog (
+                               send = e_cal_dialogs_send_dragged_or_resized_component (
                                                toplevel, client, comp, &strip_alarms, &only_new_attendees);
 
                        if (send == GTK_RESPONSE_CANCEL) {
@@ -9181,7 +9176,7 @@ e_day_view_on_top_canvas_drag_data_received (GtkWidget *widget,
 
                        e_cal_component_commit_sequence (comp);
                        if (e_cal_component_has_recurrences (comp)) {
-                               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+                               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                                        gtk_widget_queue_draw (day_view->top_canvas);
                                        g_object_unref (comp);
                                        return;
@@ -9364,7 +9359,7 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
 
                        if ((itip_organizer_is_user (registry, comp, client) ||
                             itip_sentby_is_user (registry, comp, client)))
-                               send = send_dragged_or_resized_component_dialog (
+                               send = e_cal_dialogs_send_dragged_or_resized_component (
                                                toplevel, client, comp, &strip_alarms, &only_new_attendees);
 
                        if (send == GTK_RESPONSE_CANCEL) {
@@ -9399,7 +9394,7 @@ e_day_view_on_main_canvas_drag_data_received (GtkWidget *widget,
 
                        e_cal_component_commit_sequence (comp);
                        if (e_cal_component_has_recurrences (comp)) {
-                               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+                               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                                        gtk_widget_queue_draw (day_view->main_canvas);
                                        g_object_unref (comp);
                                        return;
diff --git a/calendar/gui/e-memo-table.c b/calendar/gui/e-memo-table.c
index 2ff3cdc..8581448 100644
--- a/calendar/gui/e-memo-table.c
+++ b/calendar/gui/e-memo-table.c
@@ -36,8 +36,7 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 
-#include "dialogs/delete-comp.h"
-#include "dialogs/memo-editor.h"
+#include "e-cal-dialogs.h"
 #include "e-cal-model-memos.h"
 #include "e-cal-ops.h"
 #include "e-calendar-view.h"
@@ -892,7 +891,7 @@ memo_table_delete_selection (ESelectable *selectable)
        }
 
        if (e_cal_model_get_confirm_delete (model))
-               delete = delete_component_dialog (
+               delete = e_cal_dialogs_delete_component (
                        comp, FALSE, n_selected,
                        E_CAL_COMPONENT_JOURNAL,
                        GTK_WIDGET (memo_table));
diff --git a/calendar/gui/dialogs/e-send-options-utils.c b/calendar/gui/e-send-options-utils.c
similarity index 99%
rename from calendar/gui/dialogs/e-send-options-utils.c
rename to calendar/gui/e-send-options-utils.c
index 13df7df..6afef2f 100644
--- a/calendar/gui/dialogs/e-send-options-utils.c
+++ b/calendar/gui/e-send-options-utils.c
@@ -242,4 +242,3 @@ e_send_options_utils_fill_component (ESendOptionsDialog *sod,
        icalproperty_set_x_name (prop, "X-EVOLUTION-OPTIONS-COMPLETED");
        icalcomponent_add_property (icalcomp, prop);
 }
-
diff --git a/calendar/gui/dialogs/e-send-options-utils.h b/calendar/gui/e-send-options-utils.h
similarity index 100%
rename from calendar/gui/dialogs/e-send-options-utils.h
rename to calendar/gui/e-send-options-utils.h
diff --git a/calendar/gui/e-task-table.c b/calendar/gui/e-task-table.c
index 7b340ed..24bfe35 100644
--- a/calendar/gui/e-task-table.c
+++ b/calendar/gui/e-task-table.c
@@ -38,12 +38,12 @@
 #include <gdk/gdkkeysyms.h>
 
 #include "calendar-config.h"
-#include "dialogs/delete-comp.h"
-#include "dialogs/task-editor.h"
+#include "e-cal-dialogs.h"
 #include "e-cal-model-tasks.h"
 #include "e-cal-ops.h"
 #include "e-calendar-view.h"
 #include "e-cell-date-edit-text.h"
+#include "itip-utils.h"
 #include "print.h"
 #include "misc.h"
 
@@ -1265,9 +1265,7 @@ task_table_delete_selection (ESelectable *selectable)
                gchar *retract_comment = NULL;
                gboolean retract = FALSE;
 
-               delete = prompt_retract_dialog (
-                       comp, &retract_comment,
-                       GTK_WIDGET (task_table), &retract);
+               delete = e_cal_dialogs_prompt_retract (GTK_WIDGET (task_table), comp, &retract_comment, 
&retract);
                if (retract) {
                        icalcomponent *icalcomp = NULL;
 
@@ -1280,7 +1278,7 @@ task_table_delete_selection (ESelectable *selectable)
 
                g_free (retract_comment);
        } else if (e_cal_model_get_confirm_delete (model))
-               delete = delete_component_dialog (
+               delete = e_cal_dialogs_delete_component (
                        comp, FALSE, n_selected,
                        E_CAL_COMPONENT_TODO,
                        GTK_WIDGET (task_table));
diff --git a/calendar/gui/e-week-view.c b/calendar/gui/e-week-view.c
index 68e9ad8..0c31e85 100644
--- a/calendar/gui/e-week-view.c
+++ b/calendar/gui/e-week-view.c
@@ -35,14 +35,9 @@
 #include <glib/gi18n.h>
 #include <libgnomecanvas/libgnomecanvas.h>
 
-#include "dialogs/delete-comp.h"
-#include "dialogs/send-comp.h"
-#include "dialogs/cancel-comp.h"
-#include "dialogs/recur-comp.h"
-#include "dialogs/goto-dialog.h"
-
 #include "calendar-config.h"
 #include "comp-util.h"
+#include "e-cal-dialogs.h"
 #include "e-cal-model-calendar.h"
 #include "e-cal-ops.h"
 #include "e-week-view-event-item.h"
@@ -4372,7 +4367,7 @@ e_week_view_change_event_time (EWeekView *week_view,
        week_view->last_edited_comp_string = e_cal_component_get_as_string (comp);
 
        if (e_cal_component_has_recurrences (comp)) {
-               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                        gtk_widget_queue_draw (week_view->main_canvas);
                        goto out;
                }
@@ -4542,7 +4537,7 @@ e_week_view_on_editing_stopped (EWeekView *week_view,
                        ECalObjModType mod = E_CAL_OBJ_MOD_ALL;
 
                        if (e_cal_component_has_recurrences (comp)) {
-                               if (!recur_component_dialog (client, comp, &mod, NULL, FALSE)) {
+                               if (!e_cal_dialogs_recur_component (client, comp, &mod, NULL, FALSE)) {
                                        goto out;
                                }
 
diff --git a/calendar/gui/ea-cal-view.c b/calendar/gui/ea-cal-view.c
index 1fc0d70..3c0d238 100644
--- a/calendar/gui/ea-cal-view.c
+++ b/calendar/gui/ea-cal-view.c
@@ -28,7 +28,6 @@
 #include "e-day-view.h"
 #include "e-week-view.h"
 #include "e-cal-ops.h"
-#include "dialogs/goto-dialog.h"
 #include <glib/gi18n.h>
 
 static AtkObject * ea_cal_view_get_parent (AtkObject *accessible);
diff --git a/calendar/gui/itip-utils.c b/calendar/gui/itip-utils.c
index 4c03d00..b958978 100644
--- a/calendar/gui/itip-utils.c
+++ b/calendar/gui/itip-utils.c
@@ -24,15 +24,16 @@
 #endif
 
 #include <time.h>
-#include <glib/gi18n.h>
+#include <glib/gi18n-lib.h>
 #include <libical/ical.h>
 #include <libsoup/soup.h>
 
 #include <composer/e-msg-composer.h>
 
-#include "itip-utils.h"
-#include "dialogs/comp-editor-util.h"
 #include "calendar-config.h"
+#include "comp-util.h"
+
+#include "itip-utils.h"
 
 #define d(x)
 
@@ -745,7 +746,7 @@ comp_to_list (ESourceRegistry *registry,
                                method == E_CAL_COMPONENT_METHOD_REQUEST)
                                continue;
                        else if (only_attendees &&
-                               !comp_editor_have_in_new_attendees_lst (
+                               !cal_comp_util_have_in_new_attendees (
                                only_attendees, itip_strip_mailto (att->value)))
                                continue;
 
@@ -795,7 +796,7 @@ comp_to_list (ESourceRegistry *registry,
                                         att->cutype != ICAL_CUTYPE_UNKNOWN)
                                        continue;
                                else if (only_attendees &&
-                                       !comp_editor_have_in_new_attendees_lst (
+                                       !cal_comp_util_have_in_new_attendees (
                                        only_attendees, itip_strip_mailto (att->value)))
                                        continue;
                                else if (organizer.value &&
@@ -1541,6 +1542,21 @@ comp_compliant (ESourceRegistry *registry,
        return clone;
 }
 
+void
+itip_cal_mime_attach_free (gpointer ptr)
+{
+       struct CalMimeAttach *mime_attach = ptr;
+
+       if (mime_attach) {
+               g_free (mime_attach->filename);
+               g_free (mime_attach->content_type);
+               g_free (mime_attach->content_id);
+               g_free (mime_attach->description);
+               g_free (mime_attach->encoded_data);
+               g_free (mime_attach);
+       }
+}
+
 static void
 append_cal_attachments (EMsgComposer *composer,
                         ECalComponent *comp,
@@ -1575,16 +1591,9 @@ append_cal_attachments (EMsgComposer *composer,
                                attachment, "attachment");
                e_msg_composer_attach (composer, attachment);
                g_object_unref (attachment);
-
-               g_free (mime_attach->filename);
-               g_free (mime_attach->content_type);
-               g_free (mime_attach->content_id);
-               g_free (mime_attach->description);
-               g_free (mime_attach->encoded_data);
-               g_free (mime_attach);
        }
 
-       g_slist_free (attach_list);
+       g_slist_free_full (attach_list, itip_cal_mime_attach_free);
 }
 
 static ESource *
@@ -1681,20 +1690,25 @@ typedef struct {
        gboolean only_new_attendees;
        gboolean ensure_master_object;
 
-       gboolean finished;
+       gboolean completed;
        gboolean success;
+
+       GError *async_error;
 } ItipSendComponentData;
 
 static void
-itip_send_component_data_free (ItipSendComponentData *isc)
+itip_send_component_data_free (gpointer ptr)
 {
+       ItipSendComponentData *isc = ptr;
+
        if (isc) {
                g_clear_object (&isc->registry);
                g_clear_object (&isc->send_comp);
                g_clear_object (&isc->cal_client);
+               g_clear_error (&isc->async_error);
                if (isc->zones)
                        icalcomponent_free (isc->zones);
-               g_slist_free_full (isc->attachments_list, g_object_unref); /* CamelMimePart */
+               g_slist_free_full (isc->attachments_list, itip_cal_mime_attach_free); /* CamelMimePart */
                g_slist_free_full (isc->users, g_free);
                g_free (isc);
        }
@@ -1707,11 +1721,11 @@ itip_send_component_begin (ItipSendComponentData *isc,
 {
        g_return_if_fail (isc != NULL);
 
-       isc->finished = FALSE;
+       isc->completed = FALSE;
 
        if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_client_check_save_schedules 
(isc->cal_client)) {
                isc->success = TRUE;
-               isc->finished = TRUE;
+               isc->completed = TRUE;
                return;
        }
 
@@ -1737,7 +1751,7 @@ itip_send_component_begin (ItipSendComponentData *isc,
                d (printf ("itip-utils.c: itip_send_component_begin: calling comp_server_send_sync... \n"));
                if (!comp_server_send_sync (isc->method, isc->send_comp, isc->cal_client, isc->zones, 
&isc->users, cancellable, error)) {
                        isc->success = FALSE;
-                       isc->finished = TRUE;
+                       isc->completed = TRUE;
                        return;
                }
        }
@@ -1746,12 +1760,12 @@ itip_send_component_begin (ItipSendComponentData *isc,
        if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH &&
            e_client_check_capability (E_CLIENT (isc->cal_client), CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
                isc->success = TRUE;
-               isc->finished = TRUE;
+               isc->completed = TRUE;
        }
 }
 
 static void
-itip_send_component_finish (ItipSendComponentData *isc)
+itip_send_component_complete (ItipSendComponentData *isc)
 {
        GSettings *settings;
        EMsgComposer *composer;
@@ -1767,7 +1781,7 @@ itip_send_component_finish (ItipSendComponentData *isc)
 
        g_return_if_fail (isc != NULL);
 
-       if (isc->finished)
+       if (isc->completed)
                return;
 
        isc->success = FALSE;
@@ -1848,6 +1862,7 @@ itip_send_component_finish (ItipSendComponentData *isc)
        }
 
        append_cal_attachments (composer, comp, isc->attachments_list);
+       isc->attachments_list = NULL;
 
        if (isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users)
                gtk_widget_show (GTK_WIDGET (composer));
@@ -1866,12 +1881,12 @@ itip_send_component_finish (ItipSendComponentData *isc)
 }
 
 static void
-itip_send_component_finish_and_free (gpointer ptr)
+itip_send_component_complete_and_free (gpointer ptr)
 {
        ItipSendComponentData *isc = ptr;
 
        if (isc) {
-               itip_send_component_finish (isc);
+               itip_send_component_complete (isc);
                itip_send_component_data_free (isc);
        }
 }
@@ -1890,16 +1905,16 @@ itip_send_component_thread (EAlertSinkThreadJobData *job_data,
 }
 
 void
-itip_send_component (ECalModel *model,
-                    ECalComponentItipMethod method,
-                    ECalComponent *send_comp,
-                    ECalClient *cal_client,
-                    icalcomponent *zones,
-                    GSList *attachments_list,
-                    GSList *users,
-                    gboolean strip_alarms,
-                    gboolean only_new_attendees,
-                    gboolean ensure_master_object)
+itip_send_component_with_model (ECalModel *model,
+                               ECalComponentItipMethod method,
+                               ECalComponent *send_comp,
+                               ECalClient *cal_client,
+                               icalcomponent *zones,
+                               GSList *attachments_list,
+                               GSList *users,
+                               gboolean strip_alarms,
+                               gboolean only_new_attendees,
+                               gboolean ensure_master_object)
 {
        ESourceRegistry *registry;
        ECalDataModel *data_model;
@@ -1943,10 +1958,7 @@ itip_send_component (ECalModel *model,
        if (zones) {
                isc->zones = icalcomponent_new_clone (zones);
        }
-       if (attachments_list) {
-               isc->attachments_list = g_slist_copy (attachments_list);
-               g_slist_foreach (isc->attachments_list, (GFunc) g_object_ref, NULL);
-       }
+       isc->attachments_list = attachments_list;
        if (users) {
                GSList *link;
 
@@ -1959,12 +1971,12 @@ itip_send_component (ECalModel *model,
        isc->only_new_attendees = only_new_attendees;
        isc->ensure_master_object = ensure_master_object;
        isc->success = FALSE;
-       isc->finished = FALSE;
+       isc->completed = FALSE;
 
        display_name = e_util_get_source_full_name (registry, source);
        cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
                display_name, itip_send_component_thread,
-               isc, itip_send_component_finish_and_free);
+               isc, itip_send_component_complete_and_free);
 
        g_clear_object (&cancellable);
        g_free (display_name);
@@ -1997,17 +2009,91 @@ itip_send_comp_sync (ESourceRegistry *registry,
        isc.strip_alarms = strip_alarms;
        isc.only_new_attendees = only_new_attendees;
 
-       isc.finished = FALSE;
+       isc.completed = FALSE;
        isc.success = FALSE;
 
        itip_send_component_begin (&isc, cancellable, error);
-       itip_send_component_finish (&isc);
+       itip_send_component_complete (&isc);
 
        g_slist_free_full (isc.users, g_free);
 
        return isc.success;
 }
 
+static void
+itip_send_component_task_thread (GTask *task,
+                                gpointer source_object,
+                                gpointer task_data,
+                                GCancellable *cancellable)
+{
+       ItipSendComponentData *isc = task_data;
+
+       g_return_if_fail (isc != NULL);
+
+       itip_send_component_begin (isc, cancellable, &isc->async_error);
+}
+
+void
+itip_send_component (ESourceRegistry *registry,
+                    ECalComponentItipMethod method,
+                    ECalComponent *send_comp,
+                    ECalClient *cal_client,
+                    icalcomponent *zones,
+                    GSList *attachments_list,
+                    GSList *users,
+                    gboolean strip_alarms,
+                    gboolean only_new_attendees,
+                    gboolean ensure_master_object,
+                    GCancellable *cancellable,
+                    GAsyncReadyCallback callback,
+                    gpointer user_data)
+{
+       GTask *task;
+       ItipSendComponentData *isc;
+
+       isc = g_new0 (ItipSendComponentData, 1);
+       isc->registry = registry;
+       isc->method = method;
+       isc->send_comp = g_object_ref (send_comp);
+       isc->cal_client = cal_client;
+       isc->zones = zones;
+       isc->attachments_list = attachments_list;
+       isc->users = users;
+       isc->strip_alarms = strip_alarms;
+       isc->only_new_attendees = only_new_attendees;
+       isc->ensure_master_object = ensure_master_object;
+
+       isc->completed = FALSE;
+       isc->success = FALSE;
+
+       task = g_task_new (NULL, cancellable, callback, user_data);
+       g_task_set_task_data (task, isc, itip_send_component_data_free);
+       g_task_set_source_tag (task, itip_send_component);
+       g_task_run_in_thread (task, itip_send_component_task_thread);
+       g_object_unref (task);
+}
+
+gboolean
+itip_send_component_finish (GAsyncResult *result,
+                           GError **error)
+{
+       ItipSendComponentData *isc;
+
+       isc = g_task_get_task_data (G_TASK (result));
+
+       g_return_val_if_fail (isc != NULL, FALSE);
+       g_return_val_if_fail (g_async_result_is_tagged (result, itip_send_component), FALSE);
+
+       itip_send_component_complete (isc);
+
+       if (isc->async_error) {
+               g_propagate_error (error, isc->async_error);
+               isc->async_error = NULL;
+       }
+
+       return isc->success;
+}
+
 gboolean
 reply_to_calendar_comp (ESourceRegistry *registry,
                         ECalComponentItipMethod method,
diff --git a/calendar/gui/itip-utils.h b/calendar/gui/itip-utils.h
index 841e66e..981ab69 100644
--- a/calendar/gui/itip-utils.h
+++ b/calendar/gui/itip-utils.h
@@ -27,6 +27,7 @@
 G_BEGIN_DECLS
 
 typedef enum {
+       E_CAL_COMPONENT_METHOD_NONE = -1,
        E_CAL_COMPONENT_METHOD_PUBLISH,
        E_CAL_COMPONENT_METHOD_REQUEST,
        E_CAL_COMPONENT_METHOD_REPLY,
@@ -47,6 +48,8 @@ struct CalMimeAttach {
        guint length;
 };
 
+void           itip_cal_mime_attach_free       (gpointer ptr); /* struct CalMimeAttach * */
+
 gboolean       itip_get_default_name_and_address
                                                (ESourceRegistry *registry,
                                                 gchar **name,
@@ -80,7 +83,7 @@ gboolean      itip_send_comp_sync             (ESourceRegistry *registry,
                                                 gboolean only_new_attendees,
                                                 GCancellable *cancellable,
                                                 GError **error);
-void           itip_send_component             (ECalModel *model,
+void           itip_send_component_with_model  (ECalModel *model,
                                                 ECalComponentItipMethod method,
                                                 ECalComponent *send_comp,
                                                 ECalClient *cal_client,
@@ -90,6 +93,21 @@ void         itip_send_component             (ECalModel *model,
                                                 gboolean strip_alarms,
                                                 gboolean only_new_attendees,
                                                 gboolean ensure_master_object);
+void           itip_send_component             (ESourceRegistry *registry,
+                                                ECalComponentItipMethod method,
+                                                ECalComponent *send_comp,
+                                                ECalClient *cal_client,
+                                                icalcomponent *zones,
+                                                GSList *attachments_list,
+                                                GSList *users,
+                                                gboolean strip_alarms,
+                                                gboolean only_new_attendees,
+                                                gboolean ensure_master_object,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       itip_send_component_finish      (GAsyncResult *result,
+                                                GError **error);
 gboolean       itip_publish_begin              (ECalComponent *pub_comp,
                                                 ECalClient *cal_client,
                                                 gboolean cloned,
diff --git a/configure.ac b/configure.ac
index 66b5bc6..e5d8044 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1539,7 +1539,6 @@ calendar/Makefile
 calendar/alarm-notify/Makefile
 calendar/importers/Makefile
 calendar/gui/Makefile
-calendar/gui/dialogs/Makefile
 composer/Makefile
 m4/Makefile
 mail/Makefile
diff --git a/e-util/e-alert-bar.c b/e-util/e-alert-bar.c
index f5533fd..6f3a058 100644
--- a/e-util/e-alert-bar.c
+++ b/e-util/e-alert-bar.c
@@ -174,9 +174,8 @@ alert_bar_show_alert (EAlertBar *alert_bar)
        image = GTK_IMAGE (alert_bar->priv->image);
        gtk_image_set_from_icon_name (image, icon_name, ICON_SIZE);
 
-       /* Avoid showing an image for one-line alerts,
-        * which are usually questions or informational. */
-       visible = have_primary_text && have_secondary_text;
+       /* Avoid showing an image for empty alerts. */
+       visible = have_primary_text || have_secondary_text;
        gtk_widget_set_visible (alert_bar->priv->image, visible);
 
        gtk_widget_show (GTK_WIDGET (alert_bar));
@@ -282,6 +281,7 @@ alert_bar_constructed (GObject *object)
        gtk_widget_show (widget);
 
        widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+       gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
        gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
        gtk_widget_show (widget);
 
diff --git a/e-util/e-widget-undo.c b/e-util/e-widget-undo.c
index a7e6f8c..9efe760 100644
--- a/e-util/e-widget-undo.c
+++ b/e-util/e-widget-undo.c
@@ -511,6 +511,7 @@ undo_reset (GObject *object)
 
        data->n_undos = 0;
        data->n_redos = 0;
+       data->current_info = NULL;
 }
 
 static void
diff --git a/modules/calendar/e-cal-base-shell-backend.c b/modules/calendar/e-cal-base-shell-backend.c
index c8b4e8b..473f822 100644
--- a/modules/calendar/e-cal-base-shell-backend.c
+++ b/modules/calendar/e-cal-base-shell-backend.c
@@ -25,11 +25,8 @@
 
 #include <libedataserver/libedataserver.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>
 #include <calendar/gui/comp-util.h>
+#include <calendar/gui/e-comp-editor.h>
 
 #include "shell/e-shell-backend.h"
 #include "shell/e-shell-view.h"
@@ -234,82 +231,15 @@ handle_uri_data_free (gpointer ptr)
                return;
 
        if (hud->cal_client) {
-               CompEditor *editor;
-
-               editor = comp_editor_find_instance (hud->comp_uid);
-
-               if (!editor) {
-                       EShell *shell;
-                       ESourceRegistry *registry;
-                       ECalComponent *comp;
-                       CompEditorFlags flags = 0;
-                       icalcomponent *icalcomp = hud->existing_icalcomp;
-                       icalproperty *icalprop;
-
-                       shell = e_shell_backend_get_shell (hud->shell_backend);
-                       registry = e_shell_get_registry (shell);
-
-                       comp = e_cal_component_new ();
-                       if (!e_cal_component_set_icalcomponent (comp, hud->existing_icalcomp)) {
-                               g_warning ("%s: Failed to set icalcomp to comp", G_STRFUNC);
-                       } else {
-                               /* The 'comp' consumed the icalcomp */
-                               hud->existing_icalcomp = NULL;
-                       }
-
-                       switch (hud->source_type) {
-                               case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
-                                       icalprop = icalcomp ? icalcomponent_get_first_property (
-                                               icalcomp, ICAL_ATTENDEE_PROPERTY) : NULL;
-                                       if (icalprop != NULL)
-                                               flags |= COMP_EDITOR_MEETING;
-
-                                       if (itip_organizer_is_user (registry, comp, hud->cal_client))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       if (itip_sentby_is_user (registry, comp, hud->cal_client))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       if (!e_cal_component_has_attendees (comp))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       editor = event_editor_new (hud->cal_client, shell, flags);
-                                       break;
-                               case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
-                                       if (e_cal_component_has_organizer (comp))
-                                               flags |= COMP_EDITOR_IS_SHARED;
-
-                                       if (itip_organizer_is_user (registry, comp, hud->cal_client))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       editor = memo_editor_new (hud->cal_client, shell, flags);
-                                       break;
-                               case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
-                                       icalprop = icalcomp ? icalcomponent_get_first_property (icalcomp, 
ICAL_ATTENDEE_PROPERTY) : NULL;
-                                       if (icalprop != NULL)
-                                               flags |= COMP_EDITOR_IS_ASSIGNED;
-
-                                       if (itip_organizer_is_user (registry, comp, hud->cal_client))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       if (!e_cal_component_has_attendees (comp))
-                                               flags |= COMP_EDITOR_USER_ORG;
-
-                                       editor = task_editor_new (hud->cal_client, shell, flags);
-                                       break;
-                               default:
-                                       g_warn_if_reached ();
-                                       break;
-                       }
-
-                       if (editor)
-                               comp_editor_edit_comp (editor, comp);
-
-                       g_object_unref (comp);
-               }
+               ECompEditor *comp_editor = NULL;
+
+               comp_editor = e_comp_editor_open_for_component (NULL,
+                       e_shell_backend_get_shell (hud->shell_backend),
+                       e_client_get_source (E_CLIENT (hud->cal_client)),
+                       hud->existing_icalcomp, 0);
 
-               if (editor)
-                       gtk_window_present (GTK_WINDOW (editor));
+               if (comp_editor)
+                       gtk_window_present (GTK_WINDOW (comp_editor));
        }
 
        if (hud->existing_icalcomp)
diff --git a/modules/calendar/e-cal-base-shell-view.c b/modules/calendar/e-cal-base-shell-view.c
index a0eb9ca..8d9a92c 100644
--- a/modules/calendar/e-cal-base-shell-view.c
+++ b/modules/calendar/e-cal-base-shell-view.c
@@ -25,7 +25,7 @@
 
 #include <camel/camel.h>
 
-#include <calendar/gui/dialogs/copy-source-dialog.h>
+#include <calendar/gui/e-cal-dialogs.h>
 
 #include "e-cal-base-shell-content.h"
 #include "e-cal-base-shell-sidebar.h"
@@ -277,7 +277,7 @@ e_cal_base_shell_view_copy_calendar (EShellView *shell_view)
        from_source = e_source_selector_ref_primary_selection (selector);
        g_return_if_fail (from_source != NULL);
 
-       copy_source_dialog (GTK_WINDOW (shell_window), model, from_source);
+       e_cal_dialogs_copy_source (GTK_WINDOW (shell_window), model, from_source);
 
        g_clear_object (&from_source);
 }
diff --git a/modules/calendar/e-cal-shell-content.c b/modules/calendar/e-cal-shell-content.c
index 0831edc..2be7201 100644
--- a/modules/calendar/e-cal-shell-content.c
+++ b/modules/calendar/e-cal-shell-content.c
@@ -34,6 +34,7 @@
 #include "calendar/gui/e-day-view.h"
 #include "calendar/gui/e-month-view.h"
 #include "calendar/gui/e-week-view.h"
+#include "calendar/gui/itip-utils.h"
 #include "calendar/gui/tag-calendar.h"
 
 #include "e-cal-base-shell-sidebar.h"
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
index acd0216..13d3e32 100644
--- a/modules/calendar/e-cal-shell-view-actions.c
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -22,8 +22,11 @@
 #include <config.h>
 #endif
 
-#include <calendar/gui/e-cal-ops.h>
-#include <calendar/gui/print.h>
+#include "calendar/gui/e-cal-dialogs.h"
+#include "calendar/gui/e-cal-ops.h"
+#include "calendar/gui/e-comp-editor.h"
+#include "calendar/gui/itip-utils.h"
+#include "calendar/gui/print.h"
 
 #include "e-cal-base-shell-view.h"
 #include "e-cal-shell-view-private.h"
@@ -126,7 +129,7 @@ action_calendar_jump_to_cb (GtkAction *action,
        e_cal_shell_content_get_current_range_dates (cal_shell_content, &range_start, &range_end);
        data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
 
-       if (goto_dialog_run (GTK_WINDOW (shell_window), data_model, &range_start, &move_type, &exact_date))
+       if (e_cal_dialogs_goto_run (GTK_WINDOW (shell_window), data_model, &range_start, &move_type, 
&exact_date))
                e_cal_shell_content_move_view_range (cal_shell_content, move_type, exact_date);
 }
 
@@ -533,7 +536,7 @@ cal_shell_view_transfer_selected (ECalShellView *cal_shell_view,
        }
 
        /* Get a destination source from the user. */
-       destination_source = select_source_dialog (
+       destination_source = e_cal_dialogs_select_source (
                GTK_WINDOW (shell_window), registry,
                E_CAL_CLIENT_SOURCE_TYPE_EVENTS, source_source);
        if (destination_source == NULL) {
@@ -686,7 +689,7 @@ action_event_delegate_cb (GtkAction *action,
 
        e_calendar_view_open_event_with_flags (
                calendar_view, event->comp_data->client, clone,
-               COMP_EDITOR_MEETING | COMP_EDITOR_DELEGATE);
+               E_COMP_EDITOR_FLAG_WITH_ATTENDEES | E_COMP_EDITOR_FLAG_DELEGATE);
 
        icalcomponent_free (clone);
        g_list_free (selected);
@@ -747,7 +750,7 @@ action_event_forward_cb (GtkAction *action,
        component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp));
        g_return_if_fail (component != NULL);
 
-       itip_send_component (e_calendar_view_get_model (calendar_view),
+       itip_send_component_with_model (e_calendar_view_get_model (calendar_view),
                E_CAL_COMPONENT_METHOD_PUBLISH, component, client,
                NULL, NULL, NULL, TRUE, FALSE, TRUE);
 
diff --git a/modules/calendar/e-cal-shell-view-memopad.c b/modules/calendar/e-cal-shell-view-memopad.c
index 22285af..6a43d4c 100644
--- a/modules/calendar/e-cal-shell-view-memopad.c
+++ b/modules/calendar/e-cal-shell-view-memopad.c
@@ -22,7 +22,8 @@
 #include <config.h>
 #endif
 
-#include <calendar/gui/e-cal-ops.h>
+#include "calendar/gui/e-cal-ops.h"
+#include "calendar/gui/itip-utils.h"
 
 #include "e-cal-shell-view-private.h"
 
@@ -50,7 +51,7 @@ action_calendar_memopad_forward_cb (GtkAction *action,
        comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (comp_data->icalcomp));
        g_return_if_fail (comp != NULL);
 
-       itip_send_component (e_memo_table_get_model (memo_table),
+       itip_send_component_with_model (e_memo_table_get_model (memo_table),
                E_CAL_COMPONENT_METHOD_PUBLISH, comp, comp_data->client,
                NULL, NULL, NULL, TRUE, FALSE, TRUE);
 
diff --git a/modules/calendar/e-cal-shell-view-private.h b/modules/calendar/e-cal-shell-view-private.h
index ef14640..435a97f 100644
--- a/modules/calendar/e-cal-shell-view-private.h
+++ b/modules/calendar/e-cal-shell-view-private.h
@@ -38,11 +38,6 @@
 #include <calendar/gui/e-day-view.h>
 #include <calendar/gui/e-week-view.h>
 #include <calendar/gui/print.h>
-#include <calendar/gui/dialogs/event-editor.h>
-#include <calendar/gui/dialogs/goto-dialog.h>
-#include <calendar/gui/dialogs/memo-editor.h>
-#include <calendar/gui/dialogs/select-source-dialog.h>
-#include <calendar/gui/dialogs/task-editor.h>
 
 #include "e-cal-base-shell-sidebar.h"
 
diff --git a/modules/calendar/e-cal-shell-view-taskpad.c b/modules/calendar/e-cal-shell-view-taskpad.c
index 12bac83..917c090 100644
--- a/modules/calendar/e-cal-shell-view-taskpad.c
+++ b/modules/calendar/e-cal-shell-view-taskpad.c
@@ -22,7 +22,8 @@
 #include <config.h>
 #endif
 
-#include <calendar/gui/e-cal-ops.h>
+#include "calendar/gui/e-cal-ops.h"
+#include "calendar/gui/itip-utils.h"
 
 #include "e-cal-shell-view-private.h"
 
@@ -73,7 +74,7 @@ action_calendar_taskpad_forward_cb (GtkAction *action,
        comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (comp_data->icalcomp));
        g_return_if_fail (comp != NULL);
 
-       itip_send_component (e_task_table_get_model (task_table),
+       itip_send_component_with_model (e_task_table_get_model (task_table),
                E_CAL_COMPONENT_METHOD_PUBLISH, comp, comp_data->client,
                NULL, NULL, NULL, TRUE, FALSE, TRUE);
 
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
index 56e2353..7f40de1 100644
--- a/modules/calendar/e-memo-shell-view-actions.c
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -22,7 +22,8 @@
 #include <config.h>
 #endif
 
-#include <calendar/gui/e-cal-ops.h>
+#include "calendar/gui/e-cal-ops.h"
+#include "calendar/gui/itip-utils.h"
 
 #include "e-cal-base-shell-view.h"
 #include "e-memo-shell-view-private.h"
@@ -76,7 +77,7 @@ action_memo_forward_cb (GtkAction *action,
        comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (comp_data->icalcomp));
        g_return_if_fail (comp != NULL);
 
-       itip_send_component (e_memo_table_get_model (memo_table),
+       itip_send_component_with_model (e_memo_table_get_model (memo_table),
                E_CAL_COMPONENT_METHOD_PUBLISH, comp,
                comp_data->client, NULL, NULL, NULL, TRUE, FALSE, TRUE);
 
diff --git a/modules/calendar/e-memo-shell-view-private.h b/modules/calendar/e-memo-shell-view-private.h
index 0134080..544631b 100644
--- a/modules/calendar/e-memo-shell-view-private.h
+++ b/modules/calendar/e-memo-shell-view-private.h
@@ -31,7 +31,6 @@
 #include "calendar/gui/comp-util.h"
 #include "calendar/gui/e-cal-component-preview.h"
 #include "calendar/gui/print.h"
-#include "calendar/gui/dialogs/memo-editor.h"
 
 #include "e-cal-base-shell-sidebar.h"
 
diff --git a/modules/calendar/e-task-shell-backend.c b/modules/calendar/e-task-shell-backend.c
index cb10dc7..068f9f3 100644
--- a/modules/calendar/e-task-shell-backend.c
+++ b/modules/calendar/e-task-shell-backend.c
@@ -24,7 +24,6 @@
 
 #include <calendar/gui/comp-util.h>
 #include <calendar/gui/e-cal-ops.h>
-#include <calendar/gui/dialogs/task-editor.h>
 
 #include "e-task-shell-view.h"
 #include "e-task-shell-backend.h"
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
index 46373f3..567181a 100644
--- a/modules/calendar/e-task-shell-view-actions.c
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -22,7 +22,8 @@
 #include <config.h>
 #endif
 
-#include <calendar/gui/e-cal-ops.h>
+#include "calendar/gui/e-cal-ops.h"
+#include "calendar/gui/itip-utils.h"
 
 #include "e-cal-base-shell-view.h"
 #include "e-task-shell-view-private.h"
@@ -99,7 +100,7 @@ action_task_forward_cb (GtkAction *action,
        comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (comp_data->icalcomp));
        g_return_if_fail (comp != NULL);
 
-       itip_send_component (e_task_table_get_model (task_table),
+       itip_send_component_with_model (e_task_table_get_model (task_table),
                E_CAL_COMPONENT_METHOD_PUBLISH, comp,
                comp_data->client, NULL, NULL, NULL, TRUE, FALSE, TRUE);
 
diff --git a/modules/calendar/e-task-shell-view-private.h b/modules/calendar/e-task-shell-view-private.h
index 43775dc..145a92a 100644
--- a/modules/calendar/e-task-shell-view-private.h
+++ b/modules/calendar/e-task-shell-view-private.h
@@ -34,7 +34,6 @@
 #include "calendar/gui/e-cal-component-preview.h"
 #include "calendar/gui/e-cal-model-tasks.h"
 #include "calendar/gui/print.h"
-#include "calendar/gui/dialogs/task-editor.h"
 
 #include "e-cal-base-shell-sidebar.h"
 
diff --git a/modules/settings/Makefile.am b/modules/settings/Makefile.am
index 507ddc9..e137bea 100644
--- a/modules/settings/Makefile.am
+++ b/modules/settings/Makefile.am
@@ -21,8 +21,6 @@ module_settings_la_SOURCES = \
        e-settings-calendar-view.h \
        e-settings-client-cache.c \
        e-settings-client-cache.h \
-       e-settings-comp-editor.c \
-       e-settings-comp-editor.h \
        e-settings-date-edit.c \
        e-settings-date-edit.h \
        e-settings-deprecated.c \
diff --git a/modules/settings/evolution-module-settings.c b/modules/settings/evolution-module-settings.c
index e6fce74..5b81a94 100644
--- a/modules/settings/evolution-module-settings.c
+++ b/modules/settings/evolution-module-settings.c
@@ -19,7 +19,6 @@
 #include "e-settings-calendar-item.h"
 #include "e-settings-calendar-view.h"
 #include "e-settings-client-cache.h"
-#include "e-settings-comp-editor.h"
 #include "e-settings-date-edit.h"
 #include "e-settings-deprecated.h"
 #include "e-settings-html-editor-view.h"
@@ -47,7 +46,6 @@ e_module_load (GTypeModule *type_module)
        e_settings_calendar_item_type_register (type_module);
        e_settings_calendar_view_type_register (type_module);
        e_settings_client_cache_type_register (type_module);
-       e_settings_comp_editor_type_register (type_module);
        e_settings_date_edit_type_register (type_module);
        e_settings_deprecated_type_register (type_module);
        e_settings_html_editor_view_type_register (type_module);
diff --git a/plugins/mail-to-task/mail-to-task.c b/plugins/mail-to-task/mail-to-task.c
index 02cdfb9..d1e7531 100644
--- a/plugins/mail-to-task/mail-to-task.c
+++ b/plugins/mail-to-task/mail-to-task.c
@@ -40,10 +40,8 @@
 #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>
+#include <calendar/gui/e-comp-editor.h>
+#include <calendar/gui/itip-utils.h>
 
 #define E_SHELL_WINDOW_ACTION_CONVERT_TO_APPOINTMENT(window) \
        E_SHELL_WINDOW_ACTION ((window), "mail-convert-to-appointment")
@@ -59,16 +57,15 @@ gboolean    mail_browser_init               (GtkUIManager *ui_manager,
 gboolean       mail_shell_view_init            (GtkUIManager *ui_manager,
                                                 EShellView *shell_view);
 
-static CompEditor *
+static ECompEditor *
 get_component_editor (EShell *shell,
                       ECalClient *client,
                       ECalComponent *comp,
                       gboolean is_new,
                       GError **error)
 {
-       ECalComponentId *id;
-       CompEditorFlags flags = 0;
-       CompEditor *editor = NULL;
+       ECompEditorFlags flags = 0;
+       ECompEditor *comp_editor = NULL;
        ESourceRegistry *registry;
 
        g_return_val_if_fail (E_IS_SHELL (shell), NULL);
@@ -77,61 +74,31 @@ get_component_editor (EShell *shell,
 
        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;
+               flags |= E_COMP_EDITOR_FLAG_IS_NEW;
        } else {
-               editor = comp_editor_find_instance (id->uid);
+               comp_editor = e_comp_editor_find_existing_for (
+                       e_client_get_source (E_CLIENT (client)),
+                       e_cal_component_get_icalcomponent (comp));
        }
 
-       if (!editor) {
+       if (!comp_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);
+                       flags |= E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
+               if (e_cal_component_has_attendees (comp))
+                       flags |= E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
 
-                       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:
-                       g_warn_if_reached ();
-                       break;
-               }
-
-               if (editor) {
-                       comp_editor_edit_comp (editor, comp);
+               comp_editor = e_comp_editor_open_for_component (NULL,
+                       shell, e_client_get_source (E_CLIENT (client)),
+                       e_cal_component_get_icalcomponent (comp), flags);
 
+               if (comp_editor) {
                        /* request save for new events */
-                       comp_editor_set_changed (editor, is_new);
+                       e_comp_editor_set_changed (comp_editor, is_new);
                }
        }
 
-       e_cal_component_free_id (id);
-
-       return editor;
+       return comp_editor;
 }
 
 static void
@@ -666,14 +633,14 @@ get_question_add_all_mails (ECalClientSourceType source_type,
 }
 
 static void
-comp_editor_closed (CompEditor *editor,
-                    gboolean accepted,
+comp_editor_closed (ECompEditor *comp_editor,
+                    gboolean saved,
                     struct _manage_comp *mc)
 {
        if (!mc)
                return;
 
-       if (!accepted && mc->mails_done < mc->mails_count)
+       if (!saved && 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
@@ -782,26 +749,25 @@ do_manage_comp_idle (struct _manage_comp *mc)
 
        if (edit_comp) {
                EShell *shell;
-               CompEditor *editor;
+               ECompEditor *comp_editor;
 
                /* FIXME Pass in the EShell instance. */
                shell = e_shell_get_default ();
-               editor = get_component_editor (
+               comp_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);
+               if (comp_editor && !error) {
+                       comp_editor_title_changed (GTK_WIDGET (comp_editor), NULL, mc);
 
                        e_signal_connect_notify (
-                               editor, "notify::title",
+                               comp_editor, "notify::title",
                                G_CALLBACK (comp_editor_title_changed), mc);
                        g_signal_connect (
-                               editor, "comp_closed",
+                               comp_editor, "editor-closed",
                                G_CALLBACK (comp_editor_closed), mc);
 
-                       gtk_window_present (GTK_WINDOW (editor));
+                       gtk_window_present (GTK_WINDOW (comp_editor));
 
                        if (edit_comp != mc->comp)
                                g_object_unref (edit_comp);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1e0eeb4..a71fe21 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -42,37 +42,6 @@ calendar/alarm-notify/alarm-queue.c
 calendar/alarm-notify/util.c
 calendar/calendar.error.xml
 calendar/gui/caltypes.xml
-calendar/gui/dialogs/alarm-dialog.c
-[type: gettext/glade]calendar/gui/dialogs/alarm-dialog.ui
-calendar/gui/dialogs/alarm-list-dialog.c
-[type: gettext/glade]calendar/gui/dialogs/alarm-list-dialog.ui
-calendar/gui/dialogs/cancel-comp.c
-calendar/gui/dialogs/changed-comp.c
-calendar/gui/dialogs/comp-editor.c
-calendar/gui/dialogs/comp-editor-page.c
-calendar/gui/dialogs/comp-editor-util.c
-calendar/gui/dialogs/copy-source-dialog.c
-calendar/gui/dialogs/delete-comp.c
-[type: gettext/glade]calendar/gui/dialogs/e-delegate-dialog.ui
-calendar/gui/dialogs/event-editor.c
-calendar/gui/dialogs/event-page.c
-[type: gettext/glade]calendar/gui/dialogs/event-page.ui
-calendar/gui/dialogs/goto-dialog.c
-[type: gettext/glade]calendar/gui/dialogs/goto-dialog.ui
-calendar/gui/dialogs/memo-editor.c
-calendar/gui/dialogs/memo-page.c
-[type: gettext/glade]calendar/gui/dialogs/memo-page.ui
-calendar/gui/dialogs/recur-comp.c
-calendar/gui/dialogs/recurrence-page.c
-[type: gettext/glade]calendar/gui/dialogs/recurrence-page.ui
-calendar/gui/dialogs/save-comp.c
-calendar/gui/dialogs/schedule-page.c
-[type: gettext/glade]calendar/gui/dialogs/schedule-page.ui
-calendar/gui/dialogs/select-source-dialog.c
-calendar/gui/dialogs/send-comp.c
-calendar/gui/dialogs/task-editor.c
-calendar/gui/dialogs/task-page.c
-[type: gettext/glade]calendar/gui/dialogs/task-page.ui
 calendar/gui/ea-cal-view.c
 calendar/gui/ea-cal-view-event.c
 calendar/gui/ea-day-view.c
@@ -83,6 +52,7 @@ calendar/gui/ea-week-view.c
 calendar/gui/ea-week-view-main-item.c
 calendar/gui/e-cal-component-preview.c
 calendar/gui/e-cal-data-model.c
+calendar/gui/e-cal-dialogs.c
 calendar/gui/e-calendar-table.etspec
 calendar/gui/e-calendar-view.c
 calendar/gui/e-cal-list-view.c
@@ -92,6 +62,16 @@ calendar/gui/e-cal-model-calendar.c
 calendar/gui/e-cal-model-tasks.c
 calendar/gui/e-cal-ops.c
 calendar/gui/e-cell-date-edit-text.c
+calendar/gui/e-comp-editor-event.c
+calendar/gui/e-comp-editor-memo.c
+calendar/gui/e-comp-editor-page-attachments.c
+calendar/gui/e-comp-editor-page-general.c
+calendar/gui/e-comp-editor-page-recurrence.c
+calendar/gui/e-comp-editor-page-reminders.c
+calendar/gui/e-comp-editor-page-schedule.c
+calendar/gui/e-comp-editor-property-parts.c
+calendar/gui/e-comp-editor-task.c
+calendar/gui/e-comp-editor.c
 calendar/gui/e-day-view.c
 calendar/gui/e-day-view-time-item.c
 calendar/gui/e-day-view-top-item.c



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]