[evolution/wip/webkit2] Make calendar editors modular and non-UI-blocking
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/wip/webkit2] Make calendar editors modular and non-UI-blocking
- Date: Wed, 2 Mar 2016 15:03:32 +0000 (UTC)
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]