aboutsummaryrefslogtreecommitdiffstats
path: root/modules/itip-formatter/itip-view.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/itip-formatter/itip-view.c')
-rw-r--r--modules/itip-formatter/itip-view.c6137
1 files changed, 6137 insertions, 0 deletions
diff --git a/modules/itip-formatter/itip-view.c b/modules/itip-formatter/itip-view.c
new file mode 100644
index 0000000000..5e655eb74d
--- /dev/null
+++ b/modules/itip-formatter/itip-view.c
@@ -0,0 +1,6137 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * JP Rosevear <jpr@novell.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <libedataserverui/libedataserverui.h>
+#include <libedataserver/libedataserver.h>
+
+#include <e-util/e-util.h>
+#include <e-util/e-unicode.h>
+#include <calendar/gui/itip-utils.h>
+#include <webkit/webkitdom.h>
+
+#include <libevolution-utils/e-alert-dialog.h>
+#include <e-util/e-mktemp.h>
+
+#include <shell/e-shell.h>
+#include <shell/e-shell-utils.h>
+
+#include <libemail-utils/mail-mt.h>
+
+#include <libemail-engine/mail-folder-cache.h>
+#include <libemail-engine/mail-tools.h>
+
+#include <mail/em-config.h>
+#include <mail/em-utils.h>
+
+#include <calendar/gui/itip-utils.h>
+
+#include "e-conflict-search-selector.h"
+#include "e-source-conflict-search.h"
+#include "itip-view.h"
+#include "e-mail-part-itip.h"
+
+#define d(x)
+
+#define MEETING_ICON "stock_new-meeting"
+
+#define ITIP_VIEW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), ITIP_TYPE_VIEW, ItipViewPrivate))
+
+G_DEFINE_TYPE (ItipView, itip_view, G_TYPE_OBJECT)
+
+typedef struct {
+ ItipViewInfoItemType type;
+ gchar *message;
+
+ guint id;
+} ItipViewInfoItem;
+
+struct _ItipViewPrivate {
+ ESourceRegistry *registry;
+ gulong source_added_id;
+ gulong source_removed_id;
+ gchar *extension_name;
+
+ ItipViewMode mode;
+ ECalClientSourceType type;
+
+ gchar *sender;
+ gchar *organizer;
+ gchar *organizer_sentby;
+ gchar *delegator;
+ gchar *attendee;
+ gchar *attendee_sentby;
+ gchar *proxy;
+
+ gchar *summary;
+
+ gchar *location;
+ gchar *status;
+ gchar *comment;
+
+ struct tm *start_tm;
+ gint start_tm_is_date : 1;
+ gchar *start_label;
+ const gchar *start_header;
+
+ struct tm *end_tm;
+ gint end_tm_is_date : 1;
+ gchar *end_label;
+ const gchar *end_header;
+
+ GSList *upper_info_items;
+ GSList *lower_info_items;
+
+ guint next_info_item_id;
+
+ gchar *description;
+
+ gint buttons_sensitive : 1;
+
+ gboolean is_recur_set;
+
+ gint needs_decline : 1;
+
+ WebKitDOMDocument *dom_document;
+ EMailPartItip *itip_part;
+
+ gchar *error;
+};
+
+#define TEXT_ROW_SENDER "text_row_sender"
+#define TABLE_ROW_SUMMARY "table_row_summary"
+#define TABLE_ROW_LOCATION "table_row_location"
+#define TABLE_ROW_START_DATE "table_row_start_time"
+#define TABLE_ROW_END_DATE "table_row_end_time"
+#define TABLE_ROW_STATUS "table_row_status"
+#define TABLE_ROW_COMMENT "table_row_comment"
+#define TABLE_ROW_DESCRIPTION "table_row_description"
+#define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
+#define TABLE_ROW_ESCB "table_row_escb"
+#define TABLE_ROW_BUTTONS "table_row_buttons"
+#define TABLE_ROW_ESCB_LABEL "table_row_escb_label"
+
+#define TABLE_BUTTONS "table_buttons"
+
+#define SELECT_ESOURCE "select_esource"
+#define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"
+
+#define CHECKBOX_RSVP "checkbox_rsvp"
+#define CHECKBOX_RECUR "checkbox_recur"
+#define CHECKBOX_UPDATE "checkbox_update"
+#define CHECKBOX_FREE_TIME "checkbox_free_time"
+#define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
+#define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"
+
+#define BUTTON_OPEN_CALENDAR "button_open_calendar"
+#define BUTTON_DECLINE "button_decline"
+#define BUTTON_DECLINE_ALL "button_decline_all"
+#define BUTTON_ACCEPT "button_accept"
+#define BUTTON_ACCEPT_ALL "button_accept_all"
+#define BUTTON_TENTATIVE "button_tentative"
+#define BUTTON_TENTATIVE_ALL "button_tentative_all"
+#define BUTTON_SEND_INFORMATION "button_send_information"
+#define BUTTON_UPDATE "button_update"
+#define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
+#define BUTTON_SAVE "button_save"
+
+#define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
+#define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"
+
+#define DIV_ITIP_CONTENT "div_itip_content"
+#define DIV_ITIP_ERROR "div_itip_error"
+
+enum {
+ PROP_0,
+ PROP_EXTENSION_NAME,
+ PROP_REGISTRY
+};
+
+enum {
+ SOURCE_SELECTED,
+ RESPONSE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+format_date_and_time_x (struct tm *date_tm,
+ struct tm *current_tm,
+ gboolean use_24_hour_format,
+ gboolean show_midnight,
+ gboolean show_zero_seconds,
+ gboolean is_date,
+ gchar *buffer,
+ gint buffer_size)
+{
+ gchar *format;
+ struct tm tomorrow_tm, week_tm;
+
+ /* Calculate a normalized "tomorrow" */
+ tomorrow_tm = *current_tm;
+ /* Don't need this if date is in the past. Also, year assumption won't fail. */
+ if (date_tm->tm_year >= current_tm->tm_year && tomorrow_tm.tm_mday == time_days_in_month (current_tm->tm_year + 1900, current_tm->tm_mon)) {
+ tomorrow_tm.tm_mday = 1;
+ if (tomorrow_tm.tm_mon == 11) {
+ tomorrow_tm.tm_mon = 1;
+ tomorrow_tm.tm_year++;
+ } else {
+ tomorrow_tm.tm_mon++;
+ }
+ } else {
+ tomorrow_tm.tm_mday++;
+ }
+
+ /* Calculate a normalized "next seven days" */
+ week_tm = *current_tm;
+ /* Don't need this if date is in the past. Also, year assumption won't fail. */
+ if (date_tm->tm_year >= current_tm->tm_year && week_tm.tm_mday + 6 > time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon)) {
+ week_tm.tm_mday = (week_tm.tm_mday + 6) % time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon);
+ if (week_tm.tm_mon == 11) {
+ week_tm.tm_mon = 1;
+ week_tm.tm_year++;
+ } else {
+ week_tm.tm_mon++;
+ }
+ } else {
+ week_tm.tm_mday += 6;
+ }
+
+ /* Today */
+ if (date_tm->tm_mday == current_tm->tm_mday &&
+ date_tm->tm_mon == current_tm->tm_mon &&
+ date_tm->tm_year == current_tm->tm_year) {
+ if (is_date || (!show_midnight && date_tm->tm_hour == 0
+ && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
+ /* strftime format of a weekday and a date. */
+ format = _("Today");
+ } else if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a time,
+ * in 24-hour format, without seconds. */
+ format = _("Today %H:%M");
+ else
+ /* strftime format of a time,
+ * in 24-hour format. */
+ format = _("Today %H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a time,
+ * in 12-hour format, without seconds. */
+ format = _("Today %l:%M %p");
+ else
+ /* strftime format of a time,
+ * in 12-hour format. */
+ format = _("Today %l:%M:%S %p");
+ }
+
+ /* Tomorrow */
+ } else if (date_tm->tm_mday == tomorrow_tm.tm_mday &&
+ date_tm->tm_mon == tomorrow_tm.tm_mon &&
+ date_tm->tm_year == tomorrow_tm.tm_year) {
+ if (is_date || (!show_midnight && date_tm->tm_hour == 0
+ && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
+ /* strftime format of a weekday and a date. */
+ format = _("Tomorrow");
+ } else if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a time,
+ * in 24-hour format, without seconds. */
+ format = _("Tomorrow %H:%M");
+ else
+ /* strftime format of a time,
+ * in 24-hour format. */
+ format = _("Tomorrow %H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a time,
+ * in 12-hour format, without seconds. */
+ format = _("Tomorrow %l:%M %p");
+ else
+ /* strftime format of a time,
+ * in 12-hour format. */
+ format = _("Tomorrow %l:%M:%S %p");
+ }
+
+ /* Within 6 days */
+ } else if ((date_tm->tm_year >= current_tm->tm_year &&
+ date_tm->tm_mon >= current_tm->tm_mon &&
+ date_tm->tm_mday >= current_tm->tm_mday) &&
+
+ (date_tm->tm_year < week_tm.tm_year ||
+
+ (date_tm->tm_year == week_tm.tm_year &&
+ date_tm->tm_mon < week_tm.tm_mon) ||
+
+ (date_tm->tm_year == week_tm.tm_year &&
+ date_tm->tm_mon == week_tm.tm_mon &&
+ date_tm->tm_mday < week_tm.tm_mday))) {
+ if (is_date || (!show_midnight && date_tm->tm_hour == 0
+ && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
+ /* strftime format of a weekday. */
+ format = _("%A");
+ } else if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday and a
+ * time, in 24-hour format, without seconds. */
+ format = _("%A %H:%M");
+ else
+ /* strftime format of a weekday and a
+ * time, in 24-hour format. */
+ format = _("%A %H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday and a
+ * time, in 12-hour format, without seconds. */
+ format = _("%A %l:%M %p");
+ else
+ /* strftime format of a weekday and a
+ * time, in 12-hour format. */
+ format = _("%A %l:%M:%S %p");
+ }
+
+ /* This Year */
+ } else if (date_tm->tm_year == current_tm->tm_year) {
+ if (is_date || (!show_midnight && date_tm->tm_hour == 0
+ && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
+ /* strftime format of a weekday and a date
+ * without a year. */
+ format = _("%A, %B %e");
+ } else if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday, a date
+ * without a year and a time,
+ * in 24-hour format, without seconds. */
+ format = _("%A, %B %e %H:%M");
+ else
+ /* strftime format of a weekday, a date without a year
+ * and a time, in 24-hour format. */
+ format = _("%A, %B %e %H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday, a date without a year
+ * and a time, in 12-hour format, without seconds. */
+ format = _("%A, %B %e %l:%M %p");
+ else
+ /* strftime format of a weekday, a date without a year
+ * and a time, in 12-hour format. */
+ format = _("%A, %B %e %l:%M:%S %p");
+ }
+ } else {
+ if (is_date || (!show_midnight && date_tm->tm_hour == 0
+ && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
+ /* strftime format of a weekday and a date. */
+ format = _("%A, %B %e, %Y");
+ } else if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday, a date and a
+ * time, in 24-hour format, without seconds. */
+ format = _("%A, %B %e, %Y %H:%M");
+ else
+ /* strftime format of a weekday, a date and a
+ * time, in 24-hour format. */
+ format = _("%A, %B %e, %Y %H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday, a date and a
+ * time, in 12-hour format, without seconds. */
+ format = _("%A, %B %e, %Y %l:%M %p");
+ else
+ /* strftime format of a weekday, a date and a
+ * time, in 12-hour format. */
+ format = _("%A, %B %e, %Y %l:%M:%S %p");
+ }
+ }
+
+ /* strftime returns 0 if the string doesn't fit, and leaves the buffer
+ * undefined, so we set it to the empty string in that case. */
+ if (e_utf8_strftime_fix_am_pm (buffer, buffer_size, format, date_tm) == 0)
+ buffer[0] = '\0';
+}
+
+static gchar *
+dupe_first_bold (const gchar *format,
+ const gchar *first,
+ const gchar *second)
+{
+ gchar *f, *s, *res;
+
+ f = g_markup_printf_escaped ("<b>%s</b>", first ? first : "");
+ s = g_markup_escape_text (second ? second : "", -1);
+
+ res = g_strdup_printf (format, f, s);
+
+ g_free (f);
+ g_free (s);
+
+ return res;
+}
+
+static gchar *
+set_calendar_sender_text (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ const gchar *organizer, *attendee;
+ gchar *sender = NULL;
+ gchar *on_behalf_of = NULL;
+
+ priv = view->priv;
+
+ organizer = priv->organizer ? priv->organizer : _("An unknown person");
+ attendee = priv->attendee ? priv->attendee : _("An unknown person");
+
+ /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
+ if (priv->organizer && priv->proxy)
+ on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
+ else if (priv->attendee && priv->proxy)
+ on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
+
+ switch (priv->mode) {
+ case ITIP_VIEW_MODE_PUBLISH:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has published the following meeting information:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has published the following meeting information:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_REQUEST:
+ /* FIXME is the delegator stuff handled correctly here? */
+ if (priv->delegator) {
+ sender = dupe_first_bold (_("%s has delegated the following meeting to you:"), priv->delegator, NULL);
+ } else {
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s requests your presence at the following meeting:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s requests your presence at the following meeting:"), organizer, NULL);
+ }
+ break;
+ case ITIP_VIEW_MODE_ADD:
+ /* FIXME What text for this? */
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s wishes to add to an existing meeting:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s wishes to add to an existing meeting:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_REFRESH:
+ if (priv->attendee_sentby)
+ sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following meeting:"), attendee, priv->attendee_sentby);
+ else
+ sender = dupe_first_bold (_("%s wishes to receive the latest information for the following meeting:"), attendee, NULL);
+ break;
+ case ITIP_VIEW_MODE_REPLY:
+ if (priv->attendee_sentby)
+ sender = dupe_first_bold (_("%s through %s has sent back the following meeting response:"), attendee, priv->attendee_sentby);
+ else
+ sender = dupe_first_bold (_("%s has sent back the following meeting response:"), attendee, NULL);
+ break;
+ case ITIP_VIEW_MODE_CANCEL:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has canceled the following meeting:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has canceled the following meeting:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_COUNTER:
+ if (priv->attendee_sentby)
+ sender = dupe_first_bold (_("%s through %s has proposed the following meeting changes."), attendee, priv->attendee_sentby);
+ else
+ sender = dupe_first_bold (_("%s has proposed the following meeting changes:"), attendee, NULL);
+ break;
+ case ITIP_VIEW_MODE_DECLINECOUNTER:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has declined the following meeting changes:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has declined the following meeting changes:"), organizer, NULL);
+ break;
+ default:
+ break;
+ }
+
+ if (sender && on_behalf_of) {
+ gchar *tmp;
+ tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
+ g_free (sender);
+ sender = tmp;
+ }
+
+ g_free (on_behalf_of);
+
+ return sender;
+}
+
+static gchar *
+set_tasklist_sender_text (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ const gchar *organizer, *attendee;
+ gchar *sender = NULL;
+ gchar *on_behalf_of = NULL;
+
+ priv = view->priv;
+
+ organizer = priv->organizer ? priv->organizer : _("An unknown person");
+ attendee = priv->attendee ? priv->attendee : _("An unknown person");
+
+ /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
+ if (priv->organizer && priv->proxy)
+ on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
+ else if (priv->attendee && priv->proxy)
+ on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
+
+ switch (priv->mode) {
+ case ITIP_VIEW_MODE_PUBLISH:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has published the following task:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has published the following task:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_REQUEST:
+ /* FIXME is the delegator stuff handled correctly here? */
+ if (priv->delegator) {
+ sender = dupe_first_bold (_("%s requests the assignment of %s to the following task:"), organizer, priv->delegator);
+ } else {
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has assigned you a task:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has assigned you a task:"), organizer, NULL);
+ }
+ break;
+ case ITIP_VIEW_MODE_ADD:
+ /* FIXME What text for this? */
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s wishes to add to an existing task:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s wishes to add to an existing task:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_REFRESH:
+ if (priv->attendee_sentby)
+ sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following assigned task:"), attendee, priv->attendee_sentby);
+ else
+ sender = dupe_first_bold (_("%s wishes to receive the latest information for the following assigned task:"), attendee, NULL);
+ break;
+ case ITIP_VIEW_MODE_REPLY:
+ if (priv->attendee_sentby)
+ sender = dupe_first_bold (_("%s through %s has sent back the following assigned task response:"), attendee, priv->attendee_sentby);
+ else
+ sender = dupe_first_bold (_("%s has sent back the following assigned task response:"), attendee, NULL);
+ break;
+ case ITIP_VIEW_MODE_CANCEL:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has canceled the following assigned task:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has canceled the following assigned task:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_COUNTER:
+ if (priv->attendee_sentby)
+ sender = dupe_first_bold (_("%s through %s has proposed the following task assignment changes:"), attendee, priv->attendee_sentby);
+ else
+ sender = dupe_first_bold (_("%s has proposed the following task assignment changes:"), attendee, NULL);
+ break;
+ case ITIP_VIEW_MODE_DECLINECOUNTER:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has declined the following assigned task:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has declined the following assigned task:"), organizer, NULL);
+ break;
+ default:
+ break;
+ }
+
+ if (sender && on_behalf_of) {
+ gchar *tmp;
+ tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
+ g_free (sender);
+ sender = tmp;
+ }
+
+ g_free (on_behalf_of);
+
+ return sender;
+}
+
+static gchar *
+set_journal_sender_text (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ const gchar *organizer;
+ gchar *sender = NULL;
+ gchar *on_behalf_of = NULL;
+
+ priv = view->priv;
+
+ organizer = priv->organizer ? priv->organizer : _("An unknown person");
+
+ /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
+ if (priv->organizer && priv->proxy)
+ on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
+ else if (priv->attendee && priv->proxy)
+ on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
+
+ switch (priv->mode) {
+ case ITIP_VIEW_MODE_PUBLISH:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has published the following memo:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has published the following memo:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_ADD:
+ /* FIXME What text for this? */
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s wishes to add to an existing memo:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s wishes to add to an existing memo:"), organizer, NULL);
+ break;
+ case ITIP_VIEW_MODE_CANCEL:
+ if (priv->organizer_sentby)
+ sender = dupe_first_bold (_("%s through %s has canceled the following shared memo:"), organizer, priv->organizer_sentby);
+ else
+ sender = dupe_first_bold (_("%s has canceled the following shared memo:"), organizer, NULL);
+ break;
+ default:
+ break;
+ }
+
+ if (sender && on_behalf_of)
+ sender = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
+
+ g_free (on_behalf_of);
+
+ return sender;
+}
+
+static void
+set_sender_text (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ priv = view->priv;
+
+ if (priv->sender)
+ g_free (priv->sender);
+
+ switch (priv->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ priv->sender = set_calendar_sender_text (view);
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ priv->sender = set_tasklist_sender_text (view);
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ priv->sender = set_journal_sender_text (view);
+ break;
+ default:
+ priv->sender = NULL;
+ break;
+ }
+
+ if (priv->sender && priv->dom_document) {
+ WebKitDOMElement *div;
+
+ div = webkit_dom_document_get_element_by_id (
+ priv->dom_document, TEXT_ROW_SENDER);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL);
+ }
+}
+
+static void
+update_start_end_times (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ WebKitDOMElement *row, *col;
+ gchar buffer[256];
+ time_t now;
+ struct tm *now_tm;
+
+ priv = view->priv;
+
+ now = time (NULL);
+ now_tm = localtime (&now);
+
+ if (priv->start_label)
+ g_free (priv->start_label);
+ if (priv->end_label)
+ g_free (priv->end_label);
+
+ #define is_same(_member) (priv->start_tm->_member == priv->end_tm->_member)
+ if (priv->start_tm && priv->end_tm && priv->start_tm_is_date && priv->end_tm_is_date
+ && is_same (tm_mday) && is_same (tm_mon) && is_same (tm_year)) {
+ /* it's an all day event in one particular day */
+ format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
+ priv->start_label = g_strdup (buffer);
+ priv->start_header = _("All day:");
+ priv->end_header = NULL;
+ priv->end_label = NULL;
+ } else {
+ if (priv->start_tm) {
+ format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
+ priv->start_header = priv->start_tm_is_date ? _("Start day:") : _("Start time:");
+ priv->start_label = g_strdup (buffer);
+ } else {
+ priv->start_header = NULL;
+ priv->start_label = NULL;
+ }
+
+ if (priv->end_tm) {
+ format_date_and_time_x (priv->end_tm, now_tm, FALSE, TRUE, FALSE, priv->end_tm_is_date, buffer, 256);
+ priv->end_header = priv->end_tm_is_date ? _("End day:") : _("End time:");
+ priv->end_label = g_strdup (buffer);
+ } else {
+ priv->end_header = NULL;
+ priv->end_label = NULL;
+ }
+ }
+ #undef is_same
+
+ if (priv->dom_document) {
+ row = webkit_dom_document_get_element_by_id (
+ priv->dom_document, TABLE_ROW_START_DATE);
+ if (priv->start_header && priv->start_label) {
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
+
+ col = webkit_dom_element_get_first_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL);
+
+ col = webkit_dom_element_get_last_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL);
+ } else {
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
+ }
+
+ row = webkit_dom_document_get_element_by_id (
+ priv->dom_document, TABLE_ROW_END_DATE);
+ if (priv->end_header && priv->end_label) {
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
+
+ col = webkit_dom_element_get_first_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL);
+
+ col = webkit_dom_element_get_last_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL);
+ } else {
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
+ }
+ }
+}
+
+static void
+button_clicked_cb (WebKitDOMElement *element,
+ WebKitDOMEvent *event,
+ gpointer data)
+{
+ ItipViewResponse response;
+ gchar *responseStr;
+
+ responseStr = webkit_dom_html_button_element_get_value (
+ WEBKIT_DOM_HTML_BUTTON_ELEMENT (element));
+
+ response = atoi (responseStr);
+
+ //d(printf("Clicked btton %d\n", response));
+ g_signal_emit (G_OBJECT (data), signals[RESPONSE], 0, response);
+}
+
+static void
+rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
+ WebKitDOMEvent *event,
+ gpointer data)
+{
+ WebKitDOMElement *el;
+
+ ItipView *view = data;
+ gboolean rsvp;
+
+ rsvp = webkit_dom_html_input_element_get_checked (input);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
+ webkit_dom_html_text_area_element_set_disabled (
+ WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
+}
+
+static void
+recur_toggled_cb (WebKitDOMHTMLInputElement *input,
+ WebKitDOMEvent *event,
+ gpointer data)
+{
+ ItipView *view = data;
+
+ itip_view_set_mode (view, view->priv->mode);
+}
+
+/*
+ alarm_check_toggled_cb
+ check1 was changed, so make the second available based on state of the first check.
+*/
+static void
+alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
+ WebKitDOMEvent *event,
+ ItipView *view)
+{
+ WebKitDOMElement *check2;
+ gchar *id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1));
+
+ if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
+ check2 = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_KEEP_ALARM);
+ } else {
+ check2 = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
+ }
+
+ g_free (id);
+
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
+ (webkit_dom_html_element_get_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (check1)) &&
+ webkit_dom_html_input_element_get_checked (check1)));
+}
+
+static void
+source_changed_cb (WebKitDOMElement *select,
+ WebKitDOMEvent *event,
+ ItipView *view)
+{
+ ESource *source;
+
+ source = itip_view_ref_source (view);
+
+ d(printf("Source changed to '%s'\n", e_source_get_display_name (source)));
+ g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);
+
+ g_object_unref (source);
+}
+
+static gchar *
+parse_html_mnemonics (const gchar *label,
+ gchar **access_key)
+{
+ const gchar *pos = NULL;
+ gchar ak = 0;
+ GString *html_label = NULL;
+
+ pos = strstr (label, "_");
+ if (pos != NULL) {
+ ak = pos[1];
+
+ /* Convert to uppercase */
+ if (ak >= 'a')
+ ak = ak - 32;
+
+ html_label = g_string_new ("");
+ g_string_append_len (html_label, label, pos - label);
+ g_string_append_printf (html_label, "<u>%c</u>", pos[1]);
+ g_string_append (html_label, &pos[2]);
+
+ if (access_key) {
+ if (ak) {
+ *access_key = g_strdup_printf ("%c", ak);
+ } else {
+ *access_key = NULL;
+ }
+ }
+
+ } else {
+ html_label = g_string_new (label);
+
+ if (access_key) {
+ *access_key = NULL;
+ }
+ }
+
+ return g_string_free (html_label, FALSE);
+}
+
+static void
+append_checkbox_table_row (GString *buffer,
+ const gchar *name,
+ const gchar *label)
+{
+ gchar *access_key, *html_label;
+
+ html_label = parse_html_mnemonics (label, &access_key);
+
+ g_string_append_printf (
+ buffer,
+ "<tr id=\"table_row_%s\" hidden=\"\"><td colspan=\"2\">"
+ "<input type=\"checkbox\" name=\"%s\" id=\"%s\" value=\"%s\" >"
+ "<label for=\"%s\" accesskey=\"%s\">%s</label>"
+ "</td></tr>\n",
+ name, name, name, name, name,
+ access_key ? access_key : "", html_label);
+
+ g_free (html_label);
+
+ if (access_key)
+ g_free (access_key);
+}
+
+static void
+append_text_table_row (GString *buffer,
+ const gchar *id,
+ const gchar *label,
+ const gchar *value)
+{
+ if (label && *label) {
+
+ g_string_append_printf (buffer,
+ "<tr id=\"%s\" %s><th>%s</th><td>%s</td></tr>\n",
+ id, (value && *value) ? "" : "hidden=\"\"", label, value);
+
+ } else {
+
+ g_string_append_printf (
+ buffer,
+ "<tr id=\"%s\" hidden=\"\"><td colspan=\"2\"></td></tr>\n",
+ id);
+
+ }
+}
+
+static void
+append_info_item_row (ItipView *view,
+ const gchar *table_id,
+ ItipViewInfoItem *item)
+{
+ WebKitDOMElement *table;
+ WebKitDOMHTMLElement *row, *cell;
+ const gchar *icon_name;
+ gchar *id;
+
+ table = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, table_id);
+ row = webkit_dom_html_table_element_insert_row (
+ WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
+
+ id = g_strdup_printf ("%s_row_%d", table_id, item->id);
+ webkit_dom_html_element_set_id (row, id);
+ g_free (id);
+
+ switch (item->type) {
+ case ITIP_VIEW_INFO_ITEM_TYPE_INFO:
+ icon_name = GTK_STOCK_DIALOG_INFO;
+ break;
+ case ITIP_VIEW_INFO_ITEM_TYPE_WARNING:
+ icon_name = GTK_STOCK_DIALOG_WARNING;
+ break;
+ case ITIP_VIEW_INFO_ITEM_TYPE_ERROR:
+ icon_name = GTK_STOCK_DIALOG_ERROR;
+ break;
+ case ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS:
+ icon_name = GTK_STOCK_FIND;
+ break;
+ case ITIP_VIEW_INFO_ITEM_TYPE_NONE:
+ default:
+ icon_name = NULL;
+ }
+
+ cell = webkit_dom_html_table_row_element_insert_cell (
+ (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
+
+ if (icon_name) {
+ WebKitDOMElement *image;
+ gchar *icon_uri;
+
+ image = webkit_dom_document_create_element (
+ view->priv->dom_document, "IMG", NULL);
+
+ icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
+ g_free (icon_uri);
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (cell),
+ WEBKIT_DOM_NODE (image),
+ NULL);
+ }
+
+ cell = webkit_dom_html_table_row_element_insert_cell (
+ (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
+
+ webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
+
+ d(printf("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message));
+}
+
+static void
+remove_info_item_row (ItipView *view,
+ const gchar *table_id,
+ guint id)
+{
+ WebKitDOMElement *row;
+ gchar *row_id;
+
+ row_id = g_strdup_printf ("%s_row_%d", table_id, id);
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, row_id);
+ g_free (row_id);
+
+ webkit_dom_node_remove_child (
+ webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
+ WEBKIT_DOM_NODE (row),
+ NULL);
+
+ d(printf("Removed row %s_row_%d\n", table_id, id));
+}
+
+static void
+buttons_table_write_button (GString *buffer,
+ const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ ItipViewResponse response)
+{
+ gchar *access_key, *html_label;
+
+ html_label = parse_html_mnemonics (label, &access_key);
+
+ g_string_append_printf (
+ buffer,
+ "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden>"
+ "<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>"
+ "</button></td>\n",
+ name, response, name, access_key ? access_key : "" , icon,
+ GTK_ICON_SIZE_BUTTON, html_label);
+
+ g_free (html_label);
+
+ if (access_key)
+ g_free (access_key);
+}
+
+static void
+append_buttons_table (GString *buffer)
+{
+ g_string_append (buffer,
+ "<table class=\"itip buttons\" border=\"0\" "
+ "id=\"" TABLE_BUTTONS "\" cellspacing=\"6\" "
+ "cellpadding=\"0\" >"
+ "<tr id=\"" TABLE_ROW_BUTTONS "\">");
+
+ /* Everything gets the open button */
+ buttons_table_write_button (
+ buffer, BUTTON_OPEN_CALENDAR, _("_Open Calendar"),
+ GTK_STOCK_JUMP_TO, ITIP_VIEW_RESPONSE_OPEN);
+ buttons_table_write_button (
+ buffer, BUTTON_DECLINE_ALL, _("_Decline all"),
+ GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
+ buttons_table_write_button (
+ buffer, BUTTON_DECLINE, _("_Decline"),
+ GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
+ buttons_table_write_button (
+ buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
+ GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
+ buttons_table_write_button (
+ buffer, BUTTON_TENTATIVE, _("_Tentative"),
+ GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
+ buttons_table_write_button (
+ buffer, BUTTON_ACCEPT_ALL, _("A_ccept all"),
+ GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
+ buttons_table_write_button (
+ buffer, BUTTON_ACCEPT, _("A_ccept"),
+ GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
+ buttons_table_write_button (
+ buffer, BUTTON_SEND_INFORMATION, _("_Send Information"),
+ GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_REFRESH);
+ buttons_table_write_button (
+ buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
+ GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_UPDATE);
+ buttons_table_write_button (
+ buffer, BUTTON_UPDATE, _("_Update"),
+ GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_CANCEL);
+
+ g_string_append (buffer, "</tr></table>");
+}
+
+static void
+itip_view_rebuild_source_list (ItipView *view)
+{
+ ESourceRegistry *registry;
+ WebKitDOMElement *select;
+ GList *list, *link;
+ const gchar *extension_name;
+
+ d(printf("Assigning a new source list!\n"));
+
+ if (!view->priv->dom_document)
+ return;
+
+ registry = itip_view_get_registry (view);
+ extension_name = itip_view_get_extension_name (view);
+
+ select = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, SELECT_ESOURCE);
+
+ while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) {
+ webkit_dom_node_remove_child (
+ WEBKIT_DOM_NODE (select),
+ webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select)),
+ NULL);
+ }
+
+ if (extension_name == NULL)
+ return;
+
+ list = e_source_registry_list_sources (registry, extension_name);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ WebKitDOMElement *option;
+
+ option = webkit_dom_document_create_element (
+ view->priv->dom_document, "OPTION", NULL);
+ webkit_dom_html_option_element_set_value (
+ WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
+ e_source_get_uid (source));
+ webkit_dom_html_option_element_set_label (
+ WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
+ e_source_get_display_name (source));
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (option),
+ e_source_get_display_name (source), NULL);
+ webkit_dom_html_element_set_class_name (
+ WEBKIT_DOM_HTML_ELEMENT (option), "calendar");
+
+ webkit_dom_node_append_child (
+ WEBKIT_DOM_NODE (select),
+ WEBKIT_DOM_NODE (option),
+ NULL);
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+ source_changed_cb (select, NULL, view);
+}
+
+static void
+itip_view_source_added_cb (ESourceRegistry *registry,
+ ESource *source,
+ ItipView *view)
+{
+ const gchar *extension_name;
+
+ extension_name = itip_view_get_extension_name (view);
+
+ /* If we don't have an extension name set
+ * yet then disregard the signal emission. */
+ if (extension_name == NULL)
+ return;
+
+ if (e_source_has_extension (source, extension_name))
+ itip_view_rebuild_source_list (view);
+}
+
+static void
+itip_view_source_removed_cb (ESourceRegistry *registry,
+ ESource *source,
+ ItipView *view)
+{
+ const gchar *extension_name;
+
+ extension_name = itip_view_get_extension_name (view);
+
+ /* If we don't have an extension name set
+ * yet then disregard the signal emission. */
+ if (extension_name == NULL)
+ return;
+
+ if (e_source_has_extension (source, extension_name))
+ itip_view_rebuild_source_list (view);
+}
+
+static void
+itip_view_set_registry (ItipView *view,
+ ESourceRegistry *registry)
+{
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (view->priv->registry == NULL);
+
+ view->priv->registry = g_object_ref (registry);
+}
+
+static void
+itip_view_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EXTENSION_NAME:
+ itip_view_set_extension_name (
+ ITIP_VIEW (object),
+ g_value_get_string (value));
+ return;
+
+ case PROP_REGISTRY:
+ itip_view_set_registry (
+ ITIP_VIEW (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+itip_view_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EXTENSION_NAME:
+ g_value_set_string (
+ value, itip_view_get_extension_name (
+ ITIP_VIEW (object)));
+ return;
+
+ case PROP_REGISTRY:
+ g_value_set_object (
+ value, itip_view_get_registry (
+ ITIP_VIEW (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+itip_view_dispose (GObject *object)
+{
+ ItipViewPrivate *priv;
+
+ priv = ITIP_VIEW_GET_PRIVATE (object);
+
+ if (priv->registry != NULL) {
+ g_signal_handler_disconnect (
+ priv->registry, priv->source_added_id);
+ g_signal_handler_disconnect (
+ priv->registry, priv->source_removed_id);
+ g_object_unref (priv->registry);
+ priv->registry = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (itip_view_parent_class)->dispose (object);
+}
+
+static void
+itip_view_finalize (GObject *object)
+{
+ ItipViewPrivate *priv;
+ GSList *iter;
+
+ priv = ITIP_VIEW_GET_PRIVATE (object);
+
+ d(printf("Itip view finalized!\n"));
+
+ g_free (priv->extension_name);
+ g_free (priv->sender);
+ g_free (priv->organizer);
+ g_free (priv->organizer_sentby);
+ g_free (priv->delegator);
+ g_free (priv->attendee);
+ g_free (priv->attendee_sentby);
+ g_free (priv->proxy);
+ g_free (priv->summary);
+ g_free (priv->location);
+ g_free (priv->status);
+ g_free (priv->comment);
+ g_free (priv->start_tm);
+ g_free (priv->start_label);
+ g_free (priv->end_tm);
+ g_free (priv->end_label);
+ g_free (priv->description);
+ g_free (priv->error);
+
+ for (iter = priv->lower_info_items; iter; iter = iter->next) {
+ ItipViewInfoItem *item = iter->data;
+ g_free (item->message);
+ g_free (item);
+ }
+
+ g_slist_free (priv->lower_info_items);
+
+ for (iter = priv->upper_info_items; iter; iter = iter->next) {
+ ItipViewInfoItem *item = iter->data;
+ g_free (item->message);
+ g_free (item);
+ }
+
+ g_slist_free (priv->upper_info_items);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (itip_view_parent_class)->finalize (object);
+}
+
+static void
+itip_view_constructed (GObject *object)
+{
+ ItipView *view;
+ ESourceRegistry *registry;
+
+ view = ITIP_VIEW (object);
+ registry = itip_view_get_registry (view);
+
+ view->priv->source_added_id = g_signal_connect (
+ registry, "source-added",
+ G_CALLBACK (itip_view_source_added_cb), view);
+
+ view->priv->source_removed_id = g_signal_connect (
+ registry, "source-removed",
+ G_CALLBACK (itip_view_source_removed_cb), view);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (itip_view_parent_class)->constructed (object);
+}
+
+static void
+itip_view_class_init (ItipViewClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ItipViewPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = itip_view_set_property;
+ object_class->get_property = itip_view_get_property;
+ object_class->dispose = itip_view_dispose;
+ object_class->finalize = itip_view_finalize;
+ object_class->constructed = itip_view_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_REGISTRY,
+ g_param_spec_string (
+ "extension-name",
+ "Extension Name",
+ "Show only data sources with this extension",
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_REGISTRY,
+ g_param_spec_object (
+ "registry",
+ "Registry",
+ "Data source registry",
+ E_TYPE_SOURCE_REGISTRY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ signals[SOURCE_SELECTED] = g_signal_new (
+ "source_selected",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ItipViewClass, source_selected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ E_TYPE_SOURCE);
+
+ signals[RESPONSE] = g_signal_new (
+ "response",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ItipViewClass, response),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+}
+
+EMailPartItip *
+itip_view_get_mail_part (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->itip_part;
+}
+
+ESourceRegistry *
+itip_view_get_registry (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->registry;
+}
+
+const gchar *
+itip_view_get_extension_name (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->extension_name;
+}
+
+void
+itip_view_set_extension_name (ItipView *view,
+ const gchar *extension_name)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ /* Avoid unnecessary rebuilds. */
+ if (g_strcmp0 (extension_name, view->priv->extension_name) == 0)
+ return;
+
+ g_free (view->priv->extension_name);
+ view->priv->extension_name = g_strdup (extension_name);
+
+ g_object_notify (G_OBJECT (view), "extension-name");
+
+ itip_view_rebuild_source_list (view);
+}
+
+void
+itip_view_write (GString *buffer)
+{
+ g_string_append (buffer,
+ "<html>\n"
+ "<head>\n"
+ "<title>ITIP</title>\n"
+ "<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\" />\n"
+ "</head>\n"
+ "<body>\n");
+
+ g_string_append_printf (buffer,
+ "<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" />\n",
+ MEETING_ICON, GTK_ICON_SIZE_BUTTON);
+
+ g_string_append (buffer,
+ "<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");
+
+ /* The first section listing the sender */
+ /* FIXME What to do if the send and organizer do not match */
+ g_string_append (buffer,
+ "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");
+
+ g_string_append (buffer, "<hr>\n");
+
+ /* Elementary event information */
+ g_string_append (buffer,
+ "<table class=\"itip table\" border=\"0\" "
+ "cellspacing=\"5\" cellpadding=\"0\">\n");
+
+ append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
+ append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
+ append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);
+
+ g_string_append (buffer, "</table>\n");
+
+ /* Upper Info items */
+ g_string_append (buffer,
+ "<table class=\"itip info\" id=\"" TABLE_UPPER_ITIP_INFO "\" border=\"0\" "
+ "cellspacing=\"5\" cellpadding=\"0\">");
+
+ /* Description */
+ g_string_append (buffer,
+ "<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\" hidden=\"\"></div>\n");
+
+ g_string_append (buffer, "<hr>\n");
+
+ /* Lower Info items */
+ g_string_append (buffer,
+ "<table class=\"itip info\" id=\"" TABLE_LOWER_ITIP_INFO "\" border=\"0\" "
+ "cellspacing=\"5\" cellpadding=\"0\">");
+
+ g_string_append (buffer,
+ "<table class=\"itip table\" border=\"0\" "
+ "cellspacing=\"5\" cellpadding=\"0\">\n");
+
+ g_string_append (buffer,
+ "<tr id=\"" TABLE_ROW_ESCB "\" hidden=\"\""">"
+ "<th><label id=\"" TABLE_ROW_ESCB_LABEL "\" for=\"" SELECT_ESOURCE "\"></label></th>"
+ "<td><select name=\"" SELECT_ESOURCE "\" id=\"" SELECT_ESOURCE "\"></select></td>"
+ "</tr>\n");
+
+ /* RSVP area */
+ append_checkbox_table_row (buffer, CHECKBOX_RSVP, _("Send reply to sender"));
+
+ /* Comments */
+ g_string_append_printf (buffer,
+ "<tr id=\"" TABLE_ROW_RSVP_COMMENT "\" hidden=\"\">"
+ "<th>%s</th>"
+ "<td><textarea name=\"" TEXTAREA_RSVP_COMMENT "\" "
+ "id=\"" TEXTAREA_RSVP_COMMENT "\" "
+ "rows=\"3\" cols=\"40\" disabled=\"\">"
+ "</textarea></td>\n"
+ "</tr>\n",
+ _("Comment:"));
+
+ /* Updates */
+ append_checkbox_table_row (buffer, CHECKBOX_UPDATE, _("Send _updates to attendees"));
+
+ /* The recurrence check button */
+ append_checkbox_table_row (buffer, CHECKBOX_RECUR, _("_Apply to all instances"));
+ append_checkbox_table_row (buffer, CHECKBOX_FREE_TIME, _("Show time as _free"));
+ append_checkbox_table_row (buffer, CHECKBOX_KEEP_ALARM, _("_Preserve my reminder"));
+ append_checkbox_table_row (buffer, CHECKBOX_INHERIT_ALARM, _("_Inherit reminder"));
+
+ g_string_append (buffer, "</table>\n");
+
+ /* Buttons table */
+ append_buttons_table (buffer);
+
+ /* <div class="itip content" > */
+ g_string_append (buffer, "</div>\n");
+
+ g_string_append (buffer, "<div class=\"itip error\" id=\"" DIV_ITIP_ERROR "\"></div>");
+
+ g_string_append (buffer, "</body></html>");
+}
+
+void
+itip_view_write_for_printing (ItipView *view,
+ GString *buffer)
+{
+ if (view->priv->error && *view->priv->error) {
+ g_string_append (buffer, view->priv->error);
+ return;
+ }
+
+ g_string_append (buffer,
+ "<div class=\"itip print_content\" id=\"" DIV_ITIP_CONTENT "\">\n");
+
+ /* The first section listing the sender */
+ /* FIXME What to do if the send and organizer do not match */
+ g_string_append_printf (buffer,
+ "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\">%s</div>\n",
+ view->priv->sender ? view->priv->sender : "");
+
+ g_string_append (buffer, "<hr>\n");
+
+ /* Elementary event information */
+ g_string_append (buffer,
+ "<table class=\"itip table\" border=\"0\" "
+ "cellspacing=\"5\" cellpadding=\"0\">\n");
+
+ append_text_table_row (
+ buffer, TABLE_ROW_SUMMARY,
+ NULL, view->priv->summary);
+ append_text_table_row (
+ buffer, TABLE_ROW_LOCATION,
+ _("Location:"), view->priv->location);
+ append_text_table_row (
+ buffer, TABLE_ROW_START_DATE,
+ view->priv->start_header, view->priv->start_label);
+ append_text_table_row (
+ buffer, TABLE_ROW_END_DATE,
+ view->priv->end_header, view->priv->end_label);
+ append_text_table_row (
+ buffer, TABLE_ROW_STATUS,
+ _("Status:"), view->priv->status);
+ append_text_table_row (
+ buffer, TABLE_ROW_COMMENT,
+ _("Comment:"), view->priv->comment);
+
+ g_string_append (buffer, "</table>\n");
+
+ /* Description */
+ g_string_append_printf (
+ buffer,
+ "<div id=\"" TABLE_ROW_DESCRIPTION "\" "
+ "class=\"itip description\" %s>%s</div>\n",
+ view->priv->description ? "" : "hidden=\"\"", view->priv->description);
+
+ g_string_append (buffer, "</div>");
+}
+
+void
+itip_view_create_dom_bindings (ItipView *view,
+ WebKitDOMElement *element)
+{
+ WebKitDOMElement *el;
+ WebKitDOMDocument *doc;
+
+ doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+ view->priv->dom_document = doc;
+
+ el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (recur_toggled_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (rsvp_toggled_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+
+ el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE);
+ if (el) {
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "change",
+ G_CALLBACK (source_changed_cb), FALSE, view);
+ }
+}
+
+static void
+itip_view_init (ItipView *view)
+{
+ view->priv = ITIP_VIEW_GET_PRIVATE (view);
+
+}
+
+ItipView *
+itip_view_new (EMailPartItip *puri,
+ ESourceRegistry *registry)
+{
+ ItipView *view;
+
+ view = ITIP_VIEW (g_object_new (
+ ITIP_TYPE_VIEW,
+ "registry", registry,
+ NULL));
+ view->priv->itip_part = puri;
+
+ return view;
+}
+
+static void
+show_button (ItipView *view,
+ const gchar *id)
+{
+ WebKitDOMElement *button;
+
+ button = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, id);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
+}
+
+void
+itip_view_set_mode (ItipView *view,
+ ItipViewMode mode)
+{
+ WebKitDOMElement *row, *cell;
+ WebKitDOMElement *button;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ view->priv->mode = mode;
+
+ set_sender_text (view);
+
+ if (!view->priv->dom_document)
+ return;
+
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_BUTTONS);
+ cell = webkit_dom_element_get_first_element_child (row);
+ do {
+ button = webkit_dom_element_get_first_element_child (cell);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
+ } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
+
+ view->priv->is_recur_set = itip_view_get_recur_check_state (view);
+
+ /* Always visible */
+ show_button (view, BUTTON_OPEN_CALENDAR);
+
+ switch (mode) {
+ case ITIP_VIEW_MODE_PUBLISH:
+ if (view->priv->needs_decline) {
+ show_button (view, BUTTON_DECLINE);
+ }
+ show_button (view, BUTTON_ACCEPT);
+ break;
+ case ITIP_VIEW_MODE_REQUEST:
+ show_button (view, view->priv->is_recur_set ? BUTTON_DECLINE_ALL : BUTTON_DECLINE);
+ show_button (view, view->priv->is_recur_set ? BUTTON_TENTATIVE_ALL : BUTTON_TENTATIVE);
+ show_button (view, view->priv->is_recur_set ? BUTTON_ACCEPT_ALL : BUTTON_ACCEPT);
+ break;
+ case ITIP_VIEW_MODE_ADD:
+ if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+ show_button (view, BUTTON_DECLINE);
+ show_button (view, BUTTON_TENTATIVE);
+ }
+ show_button (view, BUTTON_ACCEPT);
+ break;
+ case ITIP_VIEW_MODE_REFRESH:
+ show_button (view, BUTTON_SEND_INFORMATION);
+ break;
+ case ITIP_VIEW_MODE_REPLY:
+ show_button (view, BUTTON_UPDATE_ATTENDEE_STATUS);
+ break;
+ case ITIP_VIEW_MODE_CANCEL:
+ show_button (view, BUTTON_UPDATE);
+ break;
+ case ITIP_VIEW_MODE_COUNTER:
+ case ITIP_VIEW_MODE_DECLINECOUNTER:
+ show_button (view, BUTTON_DECLINE);
+ show_button (view, BUTTON_TENTATIVE);
+ show_button (view, BUTTON_ACCEPT);
+ break;
+ default:
+ break;
+ }
+}
+
+ItipViewMode
+itip_view_get_mode (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
+
+ return view->priv->mode;
+}
+
+void
+itip_view_set_item_type (ItipView *view,
+ ECalClientSourceType type)
+{
+ WebKitDOMElement *label;
+ const gchar *header;
+ gchar *access_key, *html_label;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ view->priv->type = type;
+
+ if (!view->priv->dom_document)
+ return;
+
+ label = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_ESCB_LABEL);
+
+ switch (view->priv->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ header = _("_Calendar:");
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ header = _("_Tasks:");
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ header = _("_Memos:");
+ break;
+ default:
+ header = NULL;
+ break;
+ }
+
+ if (!header) {
+ set_sender_text (view);
+ return;
+ }
+
+ html_label = parse_html_mnemonics (header, &access_key);
+
+ webkit_dom_html_element_set_access_key (
+ WEBKIT_DOM_HTML_ELEMENT (label), access_key);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL);
+
+ g_free (html_label);
+
+ if (access_key)
+ g_free (access_key);
+
+ set_sender_text (view);
+}
+
+ECalClientSourceType
+itip_view_get_item_type (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
+
+ return view->priv->type;
+}
+
+void
+itip_view_set_organizer (ItipView *view,
+ const gchar *organizer)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->organizer)
+ g_free (view->priv->organizer);
+
+ view->priv->organizer = e_utf8_ensure_valid (organizer);
+
+ set_sender_text (view);
+}
+
+const gchar *
+itip_view_get_organizer (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->organizer;
+}
+
+void
+itip_view_set_organizer_sentby (ItipView *view,
+ const gchar *sentby)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->organizer_sentby)
+ g_free (view->priv->organizer_sentby);
+
+ view->priv->organizer_sentby = e_utf8_ensure_valid (sentby);
+
+ set_sender_text (view);
+}
+
+const gchar *
+itip_view_get_organizer_sentby (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->organizer_sentby;
+}
+
+void
+itip_view_set_attendee (ItipView *view,
+ const gchar *attendee)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->attendee)
+ g_free (view->priv->attendee);
+
+ view->priv->attendee = e_utf8_ensure_valid (attendee);
+
+ set_sender_text (view);
+}
+
+const gchar *
+itip_view_get_attendee (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->attendee;
+}
+
+void
+itip_view_set_attendee_sentby (ItipView *view,
+ const gchar *sentby)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->attendee_sentby)
+ g_free (view->priv->attendee_sentby);
+
+ view->priv->attendee_sentby = e_utf8_ensure_valid (sentby);
+
+ set_sender_text (view);
+}
+
+const gchar *
+itip_view_get_attendee_sentby (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->attendee_sentby;
+}
+
+void
+itip_view_set_proxy (ItipView *view,
+ const gchar *proxy)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->proxy)
+ g_free (view->priv->proxy);
+
+ view->priv->proxy = e_utf8_ensure_valid (proxy);
+
+ set_sender_text (view);
+}
+
+const gchar *
+itip_view_get_proxy (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->proxy;
+}
+
+void
+itip_view_set_delegator (ItipView *view,
+ const gchar *delegator)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->delegator)
+ g_free (view->priv->delegator);
+
+ view->priv->delegator = e_utf8_ensure_valid (delegator);
+
+ set_sender_text (view);
+}
+
+const gchar *
+itip_view_get_delegator (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->delegator;
+}
+
+void
+itip_view_set_summary (ItipView *view,
+ const gchar *summary)
+{
+ WebKitDOMElement *row, *col;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->summary)
+ g_free (view->priv->summary);
+
+ view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL;
+
+ if (!view->priv->dom_document)
+ return;
+
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_SUMMARY);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL));
+
+ col = webkit_dom_element_get_last_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col),
+ view->priv->summary ? view->priv->summary : "",
+ NULL);
+}
+
+const gchar *
+itip_view_get_summary (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->summary;
+}
+
+void
+itip_view_set_location (ItipView *view,
+ const gchar *location)
+{
+ WebKitDOMElement *row, *col;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->location)
+ g_free (view->priv->location);
+
+ view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL;
+
+ if (!view->priv->dom_document)
+ return;
+
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_LOCATION);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL));
+
+ col = webkit_dom_element_get_last_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col),
+ view->priv->location ? view->priv->location : "",
+ NULL);
+}
+
+const gchar *
+itip_view_get_location (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->location;
+}
+
+void
+itip_view_set_status (ItipView *view,
+ const gchar *status)
+{
+ WebKitDOMElement *row, *col;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->status)
+ g_free (view->priv->status);
+
+ view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL;
+
+ if (!view->priv->dom_document)
+ return;
+
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_STATUS);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL));
+
+ col = webkit_dom_element_get_last_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col),
+ view->priv->status ? view->priv->status : "",
+ NULL);
+}
+
+const gchar *
+itip_view_get_status (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->status;
+}
+
+void
+itip_view_set_comment (ItipView *view,
+ const gchar *comment)
+{
+ WebKitDOMElement *row, *col;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->comment)
+ g_free (view->priv->comment);
+
+ view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL;
+
+ if (!view->priv->dom_document)
+ return;
+
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_COMMENT);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL));
+
+ col = webkit_dom_element_get_last_element_child (row);
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (col),
+ view->priv->comment ? view->priv->comment : "",
+ NULL);
+}
+
+const gchar *
+itip_view_get_comment (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->comment;
+}
+
+void
+itip_view_set_description (ItipView *view,
+ const gchar *description)
+{
+ WebKitDOMElement *div;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (view->priv->description)
+ g_free (view->priv->description);
+
+ view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL;
+
+ if (!view->priv->dom_document)
+ return;
+
+ div = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_DESCRIPTION);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL));
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (div),
+ view->priv->description ? view->priv->description : "",
+ NULL);
+}
+
+const gchar *
+itip_view_get_description (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ return view->priv->description;
+}
+
+void
+itip_view_set_start (ItipView *view,
+ struct tm *start,
+ gboolean is_date)
+{
+ ItipViewPrivate *priv;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ priv = view->priv;
+
+ if (priv->start_tm && !start) {
+ g_free (priv->start_tm);
+ priv->start_tm = NULL;
+ } else if (start) {
+ if (!priv->start_tm)
+ priv->start_tm = g_new0 (struct tm, 1);
+
+ *priv->start_tm = *start;
+ }
+
+ priv->start_tm_is_date = is_date && start;
+
+ update_start_end_times (view);
+}
+
+const struct tm *
+itip_view_get_start (ItipView *view,
+ gboolean *is_date)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ if (is_date)
+ *is_date = view->priv->start_tm_is_date;
+
+ return view->priv->start_tm;
+}
+
+void
+itip_view_set_end (ItipView *view,
+ struct tm *end,
+ gboolean is_date)
+{
+ ItipViewPrivate *priv;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ priv = view->priv;
+
+ if (priv->end_tm && !end) {
+ g_free (priv->end_tm);
+ priv->end_tm = NULL;
+ } else if (end) {
+ if (!priv->end_tm)
+ priv->end_tm = g_new0 (struct tm, 1);
+
+ *priv->end_tm = *end;
+ }
+
+ priv->end_tm_is_date = is_date && end;
+
+ update_start_end_times (view);
+}
+
+const struct tm *
+itip_view_get_end (ItipView *view,
+ gboolean *is_date)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ if (is_date)
+ *is_date = view->priv->end_tm_is_date;
+
+ return view->priv->end_tm;
+}
+
+guint
+itip_view_add_upper_info_item (ItipView *view,
+ ItipViewInfoItemType type,
+ const gchar *message)
+{
+ ItipViewPrivate *priv;
+ ItipViewInfoItem *item;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
+
+ priv = view->priv;
+
+ item = g_new0 (ItipViewInfoItem, 1);
+
+ item->type = type;
+ item->message = e_utf8_ensure_valid (message);
+ item->id = priv->next_info_item_id++;
+
+ priv->upper_info_items = g_slist_append (priv->upper_info_items, item);
+
+ if (!view->priv->dom_document)
+ return item->id;
+
+ append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item);
+
+ return item->id;
+}
+
+guint
+itip_view_add_upper_info_item_printf (ItipView *view,
+ ItipViewInfoItemType type,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gchar *message;
+ guint id;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ id = itip_view_add_upper_info_item (view, type, message);
+ g_free (message);
+
+ return id;
+}
+
+void
+itip_view_remove_upper_info_item (ItipView *view,
+ guint id)
+{
+ ItipViewPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ priv = view->priv;
+
+ for (l = priv->upper_info_items; l; l = l->next) {
+ ItipViewInfoItem *item = l->data;
+
+ if (item->id == id) {
+ priv->upper_info_items = g_slist_remove (priv->upper_info_items, item);
+
+ g_free (item->message);
+ g_free (item);
+
+ if (!view->priv->dom_document)
+ remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);
+
+ return;
+ }
+ }
+}
+
+void
+itip_view_clear_upper_info_items (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ priv = view->priv;
+
+ for (l = priv->upper_info_items; l; l = l->next) {
+ ItipViewInfoItem *item = l->data;
+
+ if (view->priv->dom_document)
+ remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);
+
+ g_free (item->message);
+ g_free (item);
+ }
+
+ g_slist_free (priv->upper_info_items);
+ priv->upper_info_items = NULL;
+}
+
+guint
+itip_view_add_lower_info_item (ItipView *view,
+ ItipViewInfoItemType type,
+ const gchar *message)
+{
+ ItipViewPrivate *priv;
+ ItipViewInfoItem *item;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
+
+ priv = view->priv;
+
+ item = g_new0 (ItipViewInfoItem, 1);
+
+ item->type = type;
+ item->message = e_utf8_ensure_valid (message);
+ item->id = priv->next_info_item_id++;
+
+ priv->lower_info_items = g_slist_append (priv->lower_info_items, item);
+
+ if (!view->priv->dom_document)
+ return item->id;
+
+ append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item);
+
+ return item->id;
+}
+
+guint
+itip_view_add_lower_info_item_printf (ItipView *view,
+ ItipViewInfoItemType type,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gchar *message;
+ guint id;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ id = itip_view_add_lower_info_item (view, type, message);
+ g_free (message);
+
+ return id;
+}
+
+void
+itip_view_remove_lower_info_item (ItipView *view,
+ guint id)
+{
+ ItipViewPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ priv = view->priv;
+
+ for (l = priv->lower_info_items; l; l = l->next) {
+ ItipViewInfoItem *item = l->data;
+
+ if (item->id == id) {
+ priv->lower_info_items = g_slist_remove (priv->lower_info_items, item);
+
+ g_free (item->message);
+ g_free (item);
+
+ if (view->priv->dom_document)
+ remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);
+
+ return;
+ }
+ }
+}
+
+void
+itip_view_clear_lower_info_items (ItipView *view)
+{
+ ItipViewPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ priv = view->priv;
+
+ for (l = priv->lower_info_items; l; l = l->next) {
+ ItipViewInfoItem *item = l->data;
+
+ if (view->priv->dom_document)
+ remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);
+
+ g_free (item->message);
+ g_free (item);
+ }
+
+ g_slist_free (priv->lower_info_items);
+ priv->lower_info_items = NULL;
+}
+
+void
+itip_view_set_source (ItipView *view,
+ ESource *source)
+{
+ WebKitDOMElement *select;
+ WebKitDOMElement *row;
+ ESource *selected_source;
+ gulong i, len;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ d(printf("Settings default source '%s'\n", e_source_get_display_name (source)));
+
+ if (!view->priv->dom_document)
+ return;
+
+ row = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_ESCB);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL));
+ if (source == NULL)
+ return;
+
+ select = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, SELECT_ESOURCE);
+
+ /* <select> does not emit 'change' event when already selected
+ * <option> is re-selected, but we need to notify itip formatter,
+ * so that it would make all the buttons sensitive */
+ selected_source = itip_view_ref_source (view);
+ if (source == selected_source)
+ source_changed_cb (select, NULL, view);
+
+ if (selected_source != NULL)
+ g_object_unref (selected_source);
+
+ if (webkit_dom_html_select_element_get_disabled (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
+ webkit_dom_html_select_element_set_disabled (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
+ }
+
+ len = webkit_dom_html_select_element_get_length (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
+ for (i = 0; i < len; i++) {
+
+ WebKitDOMNode *node;
+ WebKitDOMHTMLOptionElement *option;
+ gchar *value;
+
+ node = webkit_dom_html_select_element_item (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i);
+ option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);
+
+ value = webkit_dom_html_option_element_get_value (option);
+ if (g_strcmp0 (value, e_source_get_uid (source)) == 0) {
+ webkit_dom_html_option_element_set_selected (
+ option, TRUE);
+
+ g_free (value);
+ break;
+ }
+
+ g_free (value);
+ }
+}
+
+ESource *
+itip_view_ref_source (ItipView *view)
+{
+ ESourceRegistry *registry;
+ WebKitDOMElement *select;
+ gchar *uid;
+ ESource *source;
+ gboolean disable = FALSE;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ if (!view->priv->dom_document)
+ return NULL;
+
+ select = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, SELECT_ESOURCE);
+ if (webkit_dom_html_select_element_get_disabled (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
+ webkit_dom_html_select_element_set_disabled (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
+ disable = TRUE;
+ }
+
+ uid = webkit_dom_html_select_element_get_value (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
+
+ registry = itip_view_get_registry (view);
+ source = e_source_registry_ref_source (registry, uid);
+
+ g_free (uid);
+
+ if (disable) {
+ webkit_dom_html_select_element_set_disabled (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE);
+ }
+
+ return source;
+}
+
+void
+itip_view_set_rsvp (ItipView *view,
+ gboolean rsvp)
+{
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RSVP);
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
+ webkit_dom_html_text_area_element_set_disabled (
+ WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
+}
+
+gboolean
+itip_view_get_rsvp (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_UPDATE);
+ return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
+}
+
+void
+itip_view_set_show_rsvp_check (ItipView *view,
+ gboolean show)
+{
+ WebKitDOMElement *label;
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, "table_row_" CHECKBOX_RSVP);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RSVP);
+ label = webkit_dom_element_get_next_element_sibling (el);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+ if (!show) {
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+ }
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_RSVP_COMMENT);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+}
+
+gboolean
+itip_view_get_show_rsvp_check (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RSVP);
+ return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
+}
+
+void
+itip_view_set_update (ItipView *view,
+ gboolean update)
+{
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_UPDATE);
+
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update);
+}
+
+gboolean
+itip_view_get_update (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_UPDATE);
+ return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
+}
+
+void
+itip_view_set_show_update_check (ItipView *view,
+ gboolean show)
+{
+ WebKitDOMElement *label;
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, "table_row_" CHECKBOX_UPDATE);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_UPDATE);
+ label = webkit_dom_element_get_next_element_sibling (el);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+ if (!show) {
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+ }
+}
+
+gboolean
+itip_view_get_show_update_check (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_UPDATE);
+ return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
+}
+
+void
+itip_view_set_rsvp_comment (ItipView *view,
+ const gchar *comment)
+{
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL));
+
+ if (comment) {
+ webkit_dom_html_text_area_element_set_value (
+ WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment);
+ }
+}
+
+gchar *
+itip_view_get_rsvp_comment (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
+
+ if (!view->priv->dom_document)
+ return NULL;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
+
+ if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) {
+ return NULL;
+ }
+
+ return webkit_dom_html_text_area_element_get_value (
+ WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
+}
+
+void
+itip_view_set_needs_decline (ItipView *view,
+ gboolean needs_decline)
+{
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ view->priv->needs_decline = needs_decline;
+}
+
+void
+itip_view_set_buttons_sensitive (ItipView *view,
+ gboolean sensitive)
+{
+ WebKitDOMElement *el, *cell;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ d(printf("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive"));
+
+ view->priv->buttons_sensitive = sensitive;
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_UPDATE);
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RECUR);
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_FREE_TIME);
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_KEEP_ALARM);
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RSVP);
+ webkit_dom_html_input_element_set_disabled (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
+ webkit_dom_html_text_area_element_set_disabled (
+ WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, SELECT_ESOURCE);
+ webkit_dom_html_select_element_set_disabled (
+ WEBKIT_DOM_HTML_SELECT_ELEMENT (el), !sensitive);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, TABLE_ROW_BUTTONS);
+ cell = webkit_dom_element_get_first_element_child (el);
+ do {
+ WebKitDOMElement *btn;
+ btn = webkit_dom_element_get_first_element_child (cell);
+ if (!webkit_dom_html_element_get_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (btn))) {
+ webkit_dom_html_button_element_set_disabled (
+ WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
+ }
+ } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
+}
+
+gboolean
+itip_view_get_buttons_sensitive (ItipView *view)
+{
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ return view->priv->buttons_sensitive;
+}
+
+gboolean
+itip_view_get_recur_check_state (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RECUR);
+ return webkit_dom_html_input_element_get_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
+}
+
+void
+itip_view_set_show_recur_check (ItipView *view,
+ gboolean show)
+{
+ WebKitDOMElement *label;
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, "table_row_" CHECKBOX_RECUR);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_RECUR);
+ label = webkit_dom_element_get_next_element_sibling (el);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+ if (!show) {
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+ }
+
+ /* and update state of the second check */
+ alarm_check_toggled_cb (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
+ NULL, view);
+}
+
+void
+itip_view_set_show_free_time_check (ItipView *view,
+ gboolean show)
+{
+ WebKitDOMElement *label;
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_FREE_TIME);
+ label = webkit_dom_element_get_next_element_sibling (el);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+ if (!show) {
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+ }
+
+ /* and update state of the second check */
+ alarm_check_toggled_cb (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
+ NULL, view);
+}
+
+gboolean
+itip_view_get_free_time_check_state (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_FREE_TIME);
+ return webkit_dom_html_input_element_get_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
+}
+
+void
+itip_view_set_show_keep_alarm_check (ItipView *view,
+ gboolean show)
+{
+ WebKitDOMElement *label;
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_KEEP_ALARM);
+ label = webkit_dom_element_get_next_element_sibling (el);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+ if (!show) {
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+ }
+
+ /* and update state of the second check */
+ alarm_check_toggled_cb (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
+ NULL, view);
+}
+
+gboolean
+itip_view_get_keep_alarm_check_state (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_KEEP_ALARM);
+ return webkit_dom_html_input_element_get_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
+}
+
+void
+itip_view_set_show_inherit_alarm_check (ItipView *view,
+ gboolean show)
+{
+ WebKitDOMElement *label;
+ WebKitDOMElement *el;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+
+ if (!view->priv->dom_document)
+ return;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
+ label = webkit_dom_element_get_next_element_sibling (el);
+ webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
+
+ if (!show) {
+ webkit_dom_html_input_element_set_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
+ }
+
+ /* and update state of the second check */
+ alarm_check_toggled_cb (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
+ NULL, view);
+}
+
+gboolean
+itip_view_get_inherit_alarm_check_state (ItipView *view)
+{
+ WebKitDOMElement *el;
+
+ g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
+
+ if (!view->priv->dom_document)
+ return FALSE;
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
+ return webkit_dom_html_input_element_get_checked (
+ WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
+}
+
+void
+itip_view_set_error (ItipView *view,
+ const gchar *error_html,
+ gboolean show_save_btn)
+{
+ WebKitDOMElement *content, *error;
+ GString *str;
+
+ g_return_if_fail (ITIP_IS_VIEW (view));
+ g_return_if_fail (error_html);
+
+ str = g_string_new (error_html);
+
+ if (show_save_btn) {
+ g_string_append (str,
+ "<table border=\"0\" width=\"100%\">"
+ "<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">");
+
+ buttons_table_write_button (
+ str, BUTTON_SAVE, _("_Save"),
+ GTK_STOCK_SAVE, ITIP_VIEW_RESPONSE_SAVE);
+
+ g_string_append (str, "</tr></table>");
+ }
+
+ view->priv->error = str->str;
+ g_string_free (str, FALSE);
+
+ if (!view->priv->dom_document)
+ return;
+
+ content = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, DIV_ITIP_CONTENT);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (content), TRUE);
+
+ error = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, DIV_ITIP_ERROR);
+ webkit_dom_html_element_set_hidden (
+ WEBKIT_DOM_HTML_ELEMENT (error), FALSE);
+
+ webkit_dom_html_element_set_inner_html (
+ WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL);
+
+ if (show_save_btn) {
+ WebKitDOMElement *el;
+
+ show_button (view, BUTTON_SAVE);
+
+ el = webkit_dom_document_get_element_by_id (
+ view->priv->dom_document, BUTTON_SAVE);
+ webkit_dom_event_target_add_event_listener (
+ WEBKIT_DOM_EVENT_TARGET (el), "click",
+ G_CALLBACK (button_clicked_cb), FALSE, view);
+ }
+}
+
+/******************************************************************************/
+
+typedef struct {
+ EMailPartItip *puri;
+ ItipView *view;
+ GCancellable *cancellable;
+ gboolean keep_alarm_check;
+ GHashTable *conflicts;
+
+ gchar *uid;
+ gchar *rid;
+
+ gchar *sexp;
+
+ gint count;
+} FormatItipFindData;
+
+static gboolean check_is_instance (icalcomponent *icalcomp);
+
+static icalproperty *
+find_attendee (icalcomponent *ical_comp,
+ const gchar *address)
+{
+ icalproperty *prop;
+
+ if (address == NULL)
+ return NULL;
+
+ for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
+ prop != NULL;
+ prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
+ gchar *attendee;
+ gchar *text;
+
+ attendee = icalproperty_get_value_as_string_r (prop);
+
+ if (!attendee)
+ continue;
+
+ text = g_strdup (itip_strip_mailto (attendee));
+ text = g_strstrip (text);
+ if (text && !g_ascii_strcasecmp (address, text)) {
+ g_free (text);
+ g_free (attendee);
+ break;
+ }
+ g_free (text);
+ g_free (attendee);
+ }
+
+ return prop;
+}
+
+static icalproperty *
+find_attendee_if_sentby (icalcomponent *ical_comp,
+ const gchar *address)
+{
+ icalproperty *prop;
+
+ if (address == NULL)
+ return NULL;
+
+ for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
+ prop != NULL;
+ prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
+ icalparameter *param;
+ const gchar *attendee_sentby;
+ gchar *text;
+
+ param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+ if (!param)
+ continue;
+
+ attendee_sentby = icalparameter_get_sentby (param);
+
+ if (!attendee_sentby)
+ continue;
+
+ text = g_strdup (itip_strip_mailto (attendee_sentby));
+ text = g_strstrip (text);
+ if (text && !g_ascii_strcasecmp (address, text)) {
+ g_free (text);
+ break;
+ }
+ g_free (text);
+ }
+
+ return prop;
+}
+
+static void
+find_to_address (EMailPartItip *itip_part,
+ icalcomponent *ical_comp,
+ icalparameter_partstat *status)
+{
+ ESourceRegistry *registry;
+ ESourceMailIdentity *extension;
+ GList *list, *link;
+ const gchar *extension_name;
+
+ registry = itip_part->registry;
+ extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+
+ if (itip_part->to_address != NULL)
+ return;
+
+ if (itip_part->msg != NULL && itip_part->folder != NULL) {
+ ESource *source;
+
+ source = em_utils_guess_mail_identity (
+ registry, itip_part->msg, itip_part->folder);
+
+ if (source != NULL) {
+ extension = e_source_get_extension (source, extension_name);
+
+ itip_part->to_address = e_source_mail_identity_dup_address (extension);
+
+ g_object_unref (source);
+ }
+ }
+
+ if (itip_part->to_address != NULL)
+ return;
+
+ /* Look through the list of attendees to find the user's address */
+ list = e_source_registry_list_sources (registry, extension_name);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ icalproperty *prop = NULL;
+ icalparameter *param;
+ const gchar *address;
+ gchar *text;
+
+ if (!e_source_get_enabled (source))
+ continue;
+
+ extension = e_source_get_extension (source, extension_name);
+ address = e_source_mail_identity_get_address (extension);
+
+ prop = find_attendee (ical_comp, address);
+ if (prop == NULL)
+ continue;
+
+ param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+ if (param != NULL)
+ itip_part->to_name = g_strdup (icalparameter_get_cn (param));
+
+ text = icalproperty_get_value_as_string_r (prop);
+
+ itip_part->to_address = g_strdup (itip_strip_mailto (text));
+ g_free (text);
+ g_strstrip (itip_part->to_address);
+
+ itip_part->my_address = g_strdup (address);
+
+ param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
+ if (param != NULL &&
+ icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE)
+ itip_part->no_reply_wanted = TRUE;
+
+ if (status) {
+ param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
+ *status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
+ }
+
+ break;
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+ if (itip_part->to_address != NULL)
+ return;
+
+ /* If the user's address was not found in the attendee's list,
+ * then the user might be responding on behalf of his/her delegator.
+ * In this case, we would want to go through the SENT-BY fields of
+ * the attendees to find the user's address.
+ *
+ *
+ * Note: This functionality could have been (easily) implemented
+ * in the previous loop, but it would hurt the performance for all
+ * providers in general. Hence, we choose to iterate through the
+ * accounts list again.
+ */
+
+ list = e_source_registry_list_sources (registry, extension_name);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ icalproperty *prop = NULL;
+ icalparameter *param;
+ const gchar *address;
+ gchar *text;
+
+ if (!e_source_get_enabled (source))
+ continue;
+
+ extension = e_source_get_extension (source, extension_name);
+ address = e_source_mail_identity_get_address (extension);
+
+ prop = find_attendee_if_sentby (ical_comp, address);
+ if (prop == NULL)
+ continue;
+
+ param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+ if (param != NULL)
+ itip_part->to_name = g_strdup (icalparameter_get_cn (param));
+
+ text = icalproperty_get_value_as_string_r (prop);
+
+ itip_part->to_address = g_strdup (itip_strip_mailto (text));
+ g_free (text);
+ g_strstrip (itip_part->to_address);
+
+ itip_part->my_address = g_strdup (address);
+
+ param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
+ if (param != NULL &&
+ ICAL_RSVP_FALSE == icalparameter_get_rsvp (param))
+ itip_part->no_reply_wanted = TRUE;
+
+ if (status) {
+ param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
+ *status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
+ }
+
+ break;
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+}
+
+static void
+find_from_address (EMailPartItip *pitip,
+ icalcomponent *ical_comp)
+{
+ GList *list, *link;
+ icalproperty *prop;
+ gchar *organizer;
+ icalparameter *param;
+ const gchar *extension_name;
+ const gchar *organizer_sentby;
+ gchar *organizer_clean = NULL;
+ gchar *organizer_sentby_clean = NULL;
+
+ prop = icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY);
+
+ if (!prop)
+ return;
+
+ organizer = icalproperty_get_value_as_string_r (prop);
+ if (organizer) {
+ organizer_clean = g_strdup (itip_strip_mailto (organizer));
+ organizer_clean = g_strstrip (organizer_clean);
+ g_free (organizer);
+ }
+
+ param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
+ if (param) {
+ organizer_sentby = icalparameter_get_sentby (param);
+ if (organizer_sentby) {
+ organizer_sentby_clean = g_strdup (itip_strip_mailto (organizer_sentby));
+ organizer_sentby_clean = g_strstrip (organizer_sentby_clean);
+ }
+ }
+
+ if (!(organizer_sentby_clean || organizer_clean))
+ return;
+
+ pitip->from_address = g_strdup (organizer_clean);
+
+ param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
+ if (param)
+ pitip->from_name = g_strdup (icalparameter_get_cn (param));
+
+ extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+ list = e_source_registry_list_sources (pitip->registry, extension_name);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ ESourceMailIdentity *extension;
+ const gchar *address;
+
+ if (!e_source_get_enabled (source))
+ continue;
+
+ extension = e_source_get_extension (source, extension_name);
+ address = e_source_mail_identity_get_address (extension);
+
+ if (address == NULL)
+ continue;
+
+ if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address))
+ || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) {
+ pitip->my_address = g_strdup (address);
+
+ break;
+ }
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+ g_free (organizer_sentby_clean);
+ g_free (organizer_clean);
+}
+
+static ECalComponent *
+get_real_item (EMailPartItip *pitip)
+{
+ ECalComponent *comp = NULL;
+ ESource *source;
+
+ source = e_client_get_source (E_CLIENT (pitip->current_client));
+ if (source)
+ comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source));
+
+ if (!comp) {
+ return NULL;
+ }
+
+ return e_cal_component_clone (comp);
+}
+
+static void
+adjust_item (EMailPartItip *pitip,
+ ECalComponent *comp)
+{
+ ECalComponent *real_comp;
+
+ real_comp = get_real_item (pitip);
+ if (real_comp != NULL) {
+ ECalComponentText text;
+ const gchar *string;
+ GSList *l;
+
+ e_cal_component_get_summary (real_comp, &text);
+ e_cal_component_set_summary (comp, &text);
+ e_cal_component_get_location (real_comp, &string);
+ e_cal_component_set_location (comp, string);
+ e_cal_component_get_description_list (real_comp, &l);
+ e_cal_component_set_description_list (comp, l);
+ e_cal_component_free_text_list (l);
+
+ g_object_unref (real_comp);
+ } else {
+ ECalComponentText text = {_("Unknown"), NULL};
+
+ e_cal_component_set_summary (comp, &text);
+ }
+}
+
+static void
+set_buttons_sensitive (EMailPartItip *pitip,
+ ItipView *view)
+{
+ gboolean read_only = TRUE;
+
+ if (pitip->current_client)
+ read_only = e_client_is_readonly (E_CLIENT (pitip->current_client));
+
+ itip_view_set_buttons_sensitive (view, pitip->current_client != NULL && !read_only);
+}
+
+static void
+add_failed_to_load_msg (ItipView *view,
+ ESource *source,
+ const GError *error)
+{
+ gchar *msg;
+
+ g_return_if_fail (view != NULL);
+ g_return_if_fail (source != NULL);
+ g_return_if_fail (error != NULL);
+
+ /* Translators: The first '%s' is replaced with a calendar name,
+ * the second '%s' with an error message */
+ msg = g_strdup_printf (_("Failed to load the calendar '%s' (%s)"), e_source_get_display_name (source), error->message);
+
+ itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, msg);
+
+ g_free (msg);
+}
+
+static void
+cal_opened_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ESource *source = E_SOURCE (source_object);
+ ItipView *view = user_data;
+ EMailPartItip *pitip = itip_view_get_mail_part (view);
+ ECalClientSourceType source_type;
+ EClient *client = NULL;
+ ECalClient *cal_client;
+ const gchar *uid;
+ GError *error = NULL;
+
+ e_client_utils_open_new_finish (source, result, &client, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (client == NULL);
+ g_error_free (error);
+ return;
+
+ } else if (error != NULL) {
+ g_warn_if_fail (client == NULL);
+ add_failed_to_load_msg (view, source, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (E_IS_CAL_CLIENT (client));
+
+ cal_client = E_CAL_CLIENT (client);
+ g_return_if_fail (cal_client != NULL);
+
+ uid = e_source_get_uid (source);
+ source_type = e_cal_client_get_source_type (cal_client);
+ g_hash_table_insert (
+ pitip->clients[source_type], g_strdup (uid), cal_client);
+
+ if (e_cal_client_check_recurrences_no_master (cal_client)) {
+ icalcomponent *icalcomp;
+ gboolean show_recur_check;
+
+ icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
+
+ show_recur_check = check_is_instance (icalcomp);
+ itip_view_set_show_recur_check (view, show_recur_check);
+ }
+
+ if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+ gboolean needs_decline;
+
+ needs_decline = e_client_check_capability (
+ E_CLIENT (client),
+ CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING);
+ itip_view_set_needs_decline (view, needs_decline);
+ itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
+ }
+
+ pitip->current_client = cal_client;
+
+ set_buttons_sensitive (pitip, view);
+}
+
+static void
+start_calendar_server (EMailPartItip *pitip,
+ ItipView *view,
+ ESource *source,
+ ECalClientSourceType type,
+ GAsyncReadyCallback func,
+ gpointer data)
+{
+ ECalClient *client;
+
+ g_return_if_fail (source != NULL);
+
+ client = g_hash_table_lookup (pitip->clients[type], e_source_get_uid (source));
+ if (client) {
+ pitip->current_client = client;
+
+ itip_view_remove_lower_info_item (view, pitip->progress_info_id);
+ pitip->progress_info_id = 0;
+
+ set_buttons_sensitive (pitip, view);
+
+ return;
+ }
+
+ e_client_utils_open_new (source,
+ type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? E_CLIENT_SOURCE_TYPE_EVENTS :
+ type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS ? E_CLIENT_SOURCE_TYPE_MEMOS :
+ type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ? E_CLIENT_SOURCE_TYPE_TASKS : E_CLIENT_SOURCE_TYPE_LAST,
+ TRUE, pitip->cancellable,
+ func, data);
+}
+
+static void
+start_calendar_server_by_uid (EMailPartItip *pitip,
+ ItipView *view,
+ const gchar *uid,
+ ECalClientSourceType type)
+{
+ ESource *source;
+
+ itip_view_set_buttons_sensitive (view, FALSE);
+
+ source = e_source_registry_ref_source (pitip->registry, uid);
+ if (source != NULL) {
+ start_calendar_server (
+ pitip, view, source, type, cal_opened_cb, view);
+ g_object_unref (source);
+ }
+}
+
+static void
+source_selected_cb (ItipView *view,
+ ESource *source,
+ gpointer data)
+{
+ EMailPartItip *pitip = data;
+
+ itip_view_set_buttons_sensitive (view, FALSE);
+
+ g_return_if_fail (source != NULL);
+
+ start_calendar_server (pitip, view, source, pitip->type, cal_opened_cb, view);
+}
+
+static void
+find_cal_update_ui (FormatItipFindData *fd,
+ ECalClient *cal_client)
+{
+ EMailPartItip *pitip;
+ ItipView *view;
+ ESource *source;
+
+ g_return_if_fail (fd != NULL);
+
+ pitip = fd->puri;
+ view = fd->view;
+
+ /* UI part gone */
+ if (g_cancellable_is_cancelled (fd->cancellable))
+ return;
+
+ source = cal_client ? e_client_get_source (E_CLIENT (cal_client)) : NULL;
+
+ if (cal_client && g_hash_table_lookup (fd->conflicts, cal_client)) {
+ itip_view_add_upper_info_item_printf (view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
+ _("An appointment in the calendar '%s' conflicts with this meeting"), e_source_get_display_name (source));
+ }
+
+ /* search for a master object if the detached object doesn't exist in the calendar */
+ if (pitip->current_client && pitip->current_client == cal_client) {
+ itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check);
+
+ pitip->current_client = cal_client;
+
+ /* Provide extra info, since its not in the component */
+ /* FIXME Check sequence number of meeting? */
+ /* FIXME Do we need to adjust elsewhere for the delegated calendar item? */
+ /* FIXME Need to update the fields in the view now */
+ if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH)
+ adjust_item (pitip, pitip->comp);
+
+ /* We clear everything because we don't really care
+ * about any other info/warnings now we found an
+ * existing versions */
+ itip_view_clear_lower_info_items (view);
+ pitip->progress_info_id = 0;
+
+ /* FIXME Check read only state of calendar? */
+ itip_view_add_lower_info_item_printf (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Found the appointment in the calendar '%s'"), e_source_get_display_name (source));
+
+ set_buttons_sensitive (pitip, view);
+ } else if (!pitip->current_client)
+ itip_view_set_show_keep_alarm_check (view, FALSE);
+
+ if (pitip->current_client && pitip->current_client == cal_client) {
+ if (e_cal_client_check_recurrences_no_master (pitip->current_client)) {
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
+
+ if (check_is_instance (icalcomp))
+ itip_view_set_show_recur_check (view, TRUE);
+ else
+ itip_view_set_show_recur_check (view, FALSE);
+ }
+
+ if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
+ /* TODO The static capability should be made generic to convey that the calendar contains unaccepted items */
+ if (e_client_check_capability (E_CLIENT (pitip->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
+ itip_view_set_needs_decline (view, TRUE);
+ else
+ itip_view_set_needs_decline (view, FALSE);
+
+ itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
+ }
+ }
+}
+
+static void
+decrease_find_data (FormatItipFindData *fd)
+{
+ g_return_if_fail (fd != NULL);
+
+ fd->count--;
+ d(printf ("Decreasing itip formatter search count to %d\n", fd->count));
+
+ if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) {
+ gboolean rsvp_enabled = FALSE;
+ EMailPartItip *pitip = fd->puri;
+ ItipView *view = fd->view;
+
+ itip_view_remove_lower_info_item (view, pitip->progress_info_id);
+ pitip->progress_info_id = 0;
+
+ /*
+ * Only allow replies if backend doesn't do that automatically.
+ * * Only enable it for forwarded invitiations (PUBLISH) or direct
+ * * invitiations (REQUEST), but not replies (REPLY).
+ * Replies only make sense for events with an organizer.
+ */
+ if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
+ (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) &&
+ pitip->has_organizer) {
+ rsvp_enabled = TRUE;
+ }
+ itip_view_set_show_rsvp_check (view, rsvp_enabled);
+
+ /* default is chosen in extract_itip_data() based on content of the VEVENT */
+ itip_view_set_rsvp (view, !pitip->no_reply_wanted);
+
+ if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST)
+ && !pitip->current_client) {
+ /* Reuse already declared one or rename? */
+ ESource *source = NULL;
+ const gchar *extension_name;
+
+ switch (pitip->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ extension_name = E_SOURCE_EXTENSION_CALENDAR;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+
+ source = e_source_registry_ref_default_for_extension_name (
+ pitip->registry, extension_name);
+
+ itip_view_set_extension_name (view, extension_name);
+
+ g_signal_connect (
+ view, "source_selected",
+ G_CALLBACK (source_selected_cb), pitip);
+
+ if (source != NULL) {
+ itip_view_set_source (view, source);
+ g_object_unref (source);
+
+ /* FIXME Shouldn't the buttons be sensitized here? */
+ } else {
+ itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to find any calendars"));
+ itip_view_set_buttons_sensitive (view, FALSE);
+ }
+ } else if (!pitip->current_client) {
+ switch (pitip->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
+ _("Unable to find this meeting in any calendar"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
+ _("Unable to find this task in any task list"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
+ _("Unable to find this memo in any memo list"));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ }
+
+ if (fd->count == 0) {
+ g_hash_table_destroy (fd->conflicts);
+ g_object_unref (fd->cancellable);
+ g_free (fd->uid);
+ g_free (fd->rid);
+ if (fd->sexp)
+ g_free (fd->sexp);
+ g_free (fd);
+ }
+}
+
+static void
+get_object_without_rid_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *cal_client = E_CAL_CLIENT (source_object);
+ FormatItipFindData *fd = user_data;
+ icalcomponent *icalcomp = NULL;
+ GError *error = NULL;
+
+ if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
+ icalcomp = NULL;
+
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_cancellable_is_cancelled (fd->cancellable)) {
+ g_clear_error (&error);
+ find_cal_update_ui (fd, cal_client);
+ decrease_find_data (fd);
+ return;
+ }
+
+ g_clear_error (&error);
+
+ if (icalcomp) {
+ ECalComponent *comp;
+
+ fd->puri->current_client = cal_client;
+ fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == ICAL_METHOD_REQUEST) &&
+ (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
+
+ comp = e_cal_component_new_from_icalcomponent (icalcomp);
+ if (comp) {
+ ESource *source = e_client_get_source (E_CLIENT (cal_client));
+
+ g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
+ }
+
+ find_cal_update_ui (fd, cal_client);
+ decrease_find_data (fd);
+ return;
+ }
+
+ find_cal_update_ui (fd, cal_client);
+ decrease_find_data (fd);
+}
+
+static void
+get_object_with_rid_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *cal_client = E_CAL_CLIENT (source_object);
+ FormatItipFindData *fd = user_data;
+ icalcomponent *icalcomp = NULL;
+ GError *error = NULL;
+
+ if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
+ icalcomp = NULL;
+
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_cancellable_is_cancelled (fd->cancellable)) {
+ g_clear_error (&error);
+ find_cal_update_ui (fd, cal_client);
+ decrease_find_data (fd);
+ return;
+ }
+
+ g_clear_error (&error);
+
+ if (icalcomp) {
+ ECalComponent *comp;
+
+ fd->puri->current_client = cal_client;
+ fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == ICAL_METHOD_REQUEST) &&
+ (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
+ icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
+
+ comp = e_cal_component_new_from_icalcomponent (icalcomp);
+ if (comp) {
+ ESource *source = e_client_get_source (E_CLIENT (cal_client));
+
+ g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
+ }
+
+ find_cal_update_ui (fd, cal_client);
+ decrease_find_data (fd);
+ return;
+ }
+
+ if (fd->rid && *fd->rid) {
+ e_cal_client_get_object (cal_client, fd->uid, NULL, fd->cancellable, get_object_without_rid_ready_cb, fd);
+ return;
+ }
+
+ find_cal_update_ui (fd, cal_client);
+ decrease_find_data (fd);
+}
+
+static void
+get_object_list_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *cal_client = E_CAL_CLIENT (source_object);
+ FormatItipFindData *fd = user_data;
+ GSList *objects = NULL;
+ GError *error = NULL;
+
+ if (!e_cal_client_get_object_list_finish (cal_client, result, &objects, &error))
+ objects = NULL;
+
+ if (g_cancellable_is_cancelled (fd->cancellable)) {
+ g_clear_error (&error);
+ decrease_find_data (fd);
+ return;
+ }
+
+ if (error) {
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_error_free (error);
+ decrease_find_data (fd);
+ return;
+ }
+
+ g_error_free (error);
+ } else {
+ g_hash_table_insert (fd->conflicts, cal_client, GINT_TO_POINTER (g_slist_length (objects)));
+ e_cal_client_free_icalcomp_slist (objects);
+ }
+
+ e_cal_client_get_object (cal_client, fd->uid, fd->rid, fd->cancellable, get_object_with_rid_ready_cb, fd);
+}
+
+static void
+find_cal_opened_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ESource *source = E_SOURCE (source_object);
+ FormatItipFindData *fd = user_data;
+ EMailPartItip *pitip = fd->puri;
+ ItipView *view = fd->view;
+ ECalClientSourceType source_type;
+ EClient *client = NULL;
+ ECalClient *cal_client;
+ gboolean search_for_conflicts = FALSE;
+ const gchar *extension_name;
+ const gchar *uid;
+ GError *error = NULL;
+
+ e_client_utils_open_new_finish (source, result, &client, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (client == NULL);
+ decrease_find_data (fd);
+ g_error_free (error);
+ return;
+ }
+
+ if (g_cancellable_is_cancelled (fd->cancellable)) {
+ g_clear_error (&error);
+ decrease_find_data (fd);
+ return;
+ }
+
+ if (error) {
+ /* FIXME Do we really want to warn here? If we fail
+ * to find the item, this won't be cleared but the
+ * selector might be shown */
+ g_warn_if_fail (client == NULL);
+ add_failed_to_load_msg (view, source, error);
+ decrease_find_data (fd);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (E_IS_CAL_CLIENT (client));
+
+ /* Do not process read-only calendars */
+ if (e_client_is_readonly (client)) {
+ g_object_unref (client);
+ decrease_find_data (fd);
+ return;
+ }
+
+ cal_client = E_CAL_CLIENT (client);
+ source_type = e_cal_client_get_source_type (cal_client);
+
+ uid = e_source_get_uid (source);
+ g_hash_table_insert (
+ pitip->clients[source_type], g_strdup (uid), cal_client);
+
+ extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
+ if (e_source_has_extension (source, extension_name)) {
+ ESourceConflictSearch *extension;
+
+ extension = e_source_get_extension (source, extension_name);
+ search_for_conflicts =
+ (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
+ e_source_conflict_search_get_include_me (extension);
+ }
+
+ /* Check for conflicts */
+ /* If the query fails, we'll just ignore it */
+ /* FIXME What happens for recurring conflicts? */
+ if (search_for_conflicts) {
+ e_cal_client_get_object_list (
+ cal_client, fd->sexp,
+ fd->cancellable,
+ get_object_list_ready_cb, fd);
+ return;
+ }
+
+ if (!pitip->current_client) {
+ e_cal_client_get_object (
+ cal_client, fd->uid, fd->rid,
+ fd->cancellable,
+ get_object_with_rid_ready_cb, fd);
+ return;
+ }
+
+ decrease_find_data (fd);
+}
+
+static void
+find_server (EMailPartItip *pitip,
+ ItipView *view,
+ ECalComponent *comp)
+{
+ FormatItipFindData *fd = NULL;
+ const gchar *uid;
+ gchar *rid = NULL;
+ CamelStore *parent_store;
+ ESourceRegistry *registry;
+ ESource *current_source = NULL;
+ GList *list, *link;
+ GList *conflict_list = NULL;
+ const gchar *extension_name;
+ const gchar *store_uid;
+
+ g_return_if_fail (pitip->folder != NULL);
+
+ switch (pitip->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ extension_name = E_SOURCE_EXTENSION_CALENDAR;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+
+ registry = pitip->registry;
+ list = e_source_registry_list_sources (registry, extension_name);
+
+ e_cal_component_get_uid (comp, &uid);
+ rid = e_cal_component_get_recurid_as_string (comp);
+
+ /* XXX Not sure what this was trying to do,
+ * but it propbably doesn't work anymore.
+ * Some comments would have been helpful. */
+ parent_store = camel_folder_get_parent_store (pitip->folder);
+
+ store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
+
+ itip_view_set_buttons_sensitive (view, FALSE);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ gboolean search_for_conflicts = FALSE;
+ const gchar *source_uid;
+
+ extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
+ if (e_source_has_extension (source, extension_name)) {
+ ESourceConflictSearch *extension;
+
+ extension =
+ e_source_get_extension (source, extension_name);
+ search_for_conflicts =
+ e_source_conflict_search_get_include_me (extension);
+ }
+
+ if (search_for_conflicts)
+ conflict_list = g_list_prepend (
+ conflict_list, g_object_ref (source));
+
+ if (current_source != NULL)
+ continue;
+
+ source_uid = e_source_get_uid (source);
+ if (g_strcmp0 (source_uid, store_uid) == 0) {
+ current_source = source;
+ conflict_list = g_list_prepend (
+ conflict_list, g_object_ref (source));
+
+ continue;
+ }
+ }
+
+ if (current_source) {
+ link = conflict_list;
+
+ pitip->progress_info_id = itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
+ _("Opening the calendar. Please wait..."));
+ } else {
+ link = list;
+ pitip->progress_info_id = itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
+ _("Searching for an existing version of this appointment"));
+ }
+
+ for (; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+
+ if (!fd) {
+ gchar *start = NULL, *end = NULL;
+
+ fd = g_new0 (FormatItipFindData, 1);
+ fd->puri = pitip;
+ fd->view = view;
+ fd->cancellable = g_object_ref (pitip->cancellable);
+ fd->conflicts = g_hash_table_new (g_direct_hash, g_direct_equal);
+ fd->uid = g_strdup (uid);
+ fd->rid = rid;
+ /* avoid free this at the end */
+ rid = NULL;
+
+ if (pitip->start_time && pitip->end_time) {
+ start = isodate_from_time_t (pitip->start_time);
+ end = isodate_from_time_t (pitip->end_time);
+
+ fd->sexp = g_strdup_printf ("(and (occur-in-time-range? (make-time \"%s\") (make-time \"%s\")) (not (uid? \"%s\")))",
+ start, end, icalcomponent_get_uid (pitip->ical_comp));
+ }
+
+ g_free (start);
+ g_free (end);
+ }
+ fd->count++;
+ d(printf ("Increasing itip formatter search count to %d\n", fd->count));
+
+ if (current_source == source)
+ start_calendar_server (
+ pitip, view, source, pitip->type,
+ find_cal_opened_cb, fd);
+ else
+ start_calendar_server (
+ pitip, view, source, pitip->type,
+ find_cal_opened_cb, fd);
+ }
+
+ g_list_free_full (conflict_list, (GDestroyNotify) g_object_unref);
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+ g_free (rid);
+}
+
+static gboolean
+change_status (ESourceRegistry *registry,
+ icalcomponent *ical_comp,
+ const gchar *address,
+ icalparameter_partstat status)
+{
+ icalproperty *prop;
+
+ prop = find_attendee (ical_comp, address);
+ if (prop) {
+ icalparameter *param;
+
+ icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER);
+ param = icalparameter_new_partstat (status);
+ icalproperty_add_parameter (prop, param);
+ } else {
+ icalparameter *param;
+
+ if (address != NULL) {
+ prop = icalproperty_new_attendee (address);
+ icalcomponent_add_property (ical_comp, prop);
+
+ param = icalparameter_new_role (ICAL_ROLE_OPTPARTICIPANT);
+ icalproperty_add_parameter (prop, param);
+
+ param = icalparameter_new_partstat (status);
+ icalproperty_add_parameter (prop, param);
+ } else {
+ gchar *default_name = NULL;
+ gchar *default_address = NULL;
+
+ itip_get_default_name_and_address (
+ registry, &default_name, &default_address);
+
+ prop = icalproperty_new_attendee (default_address);
+ icalcomponent_add_property (ical_comp, prop);
+
+ param = icalparameter_new_cn (default_name);
+ icalproperty_add_parameter (prop, param);
+
+ param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
+ icalproperty_add_parameter (prop, param);
+
+ param = icalparameter_new_partstat (status);
+ icalproperty_add_parameter (prop, param);
+
+ g_free (default_name);
+ g_free (default_address);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+message_foreach_part (CamelMimePart *part,
+ GSList **part_list)
+{
+ CamelDataWrapper *containee;
+ gint parts, i;
+ gint go = TRUE;
+
+ if (!part)
+ return;
+
+ *part_list = g_slist_append (*part_list, part);
+
+ containee = camel_medium_get_content (CAMEL_MEDIUM (part));
+
+ if (containee == NULL)
+ return;
+
+ /* using the object types is more accurate than using the mime/types */
+ if (CAMEL_IS_MULTIPART (containee)) {
+ parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
+ for (i = 0; go && i < parts; i++) {
+ /* Reuse already declared *parts? */
+ CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
+
+ message_foreach_part (part, part_list);
+ }
+ } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
+ message_foreach_part ((CamelMimePart *) containee, part_list);
+ }
+}
+
+static void
+attachment_load_finished (EAttachment *attachment,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ struct {
+ GFile *file;
+ gboolean done;
+ } *status = user_data;
+
+ /* Should be no need to check for error here. */
+ e_attachment_load_finish (attachment, result, NULL);
+
+ status->done = TRUE;
+}
+
+static void
+attachment_save_finished (EAttachment *attachment,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ struct {
+ GFile *file;
+ gboolean done;
+ } *status = user_data;
+
+ status->file = e_attachment_save_finish (attachment, result, &error);
+ status->done = TRUE;
+
+ /* XXX Error handling needs improvement. */
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gchar *
+get_uri_for_part (CamelMimePart *mime_part)
+{
+ EAttachment *attachment;
+ GFile *temp_directory;
+ gchar *template;
+ gchar *path;
+
+ struct {
+ GFile *file;
+ gboolean done;
+ } status;
+
+ /* XXX Error handling leaves much to be desired. */
+
+ template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
+ path = e_mkdtemp (template);
+ g_free (template);
+
+ if (path == NULL)
+ return NULL;
+
+ temp_directory = g_file_new_for_path (path);
+ g_free (path);
+
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, mime_part);
+
+ status.done = FALSE;
+
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ attachment_load_finished, &status);
+
+ /* Loading should be instantaneous since we already have
+ * the full content, but we still have to crank the main
+ * loop until the callback gets triggered. */
+ while (!status.done)
+ gtk_main_iteration ();
+
+ status.file = NULL;
+ status.done = FALSE;
+
+ e_attachment_save_async (
+ attachment, temp_directory, (GAsyncReadyCallback)
+ attachment_save_finished, &status);
+
+ /* We can't return until we have results, so crank
+ * the main loop until the callback gets triggered. */
+ while (!status.done)
+ gtk_main_iteration ();
+
+ if (status.file != NULL) {
+ path = g_file_get_path (status.file);
+ g_object_unref (status.file);
+ } else
+ path = NULL;
+
+ g_object_unref (attachment);
+ g_object_unref (temp_directory);
+
+ return path;
+}
+
+static void
+update_item_progress_info (EMailPartItip *pitip,
+ ItipView *view,
+ const gchar *message)
+{
+ if (pitip->update_item_progress_info_id) {
+ itip_view_remove_lower_info_item (view, pitip->update_item_progress_info_id);
+ pitip->update_item_progress_info_id = 0;
+
+ if (!message)
+ itip_view_set_buttons_sensitive (view, TRUE);
+ }
+
+ if (pitip->update_item_error_info_id) {
+ itip_view_remove_lower_info_item (view, pitip->update_item_error_info_id);
+ pitip->update_item_error_info_id = 0;
+ }
+
+ if (message) {
+ itip_view_set_buttons_sensitive (view, FALSE);
+ pitip->update_item_progress_info_id =
+ itip_view_add_lower_info_item (
+ view,
+ ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
+ message);
+ }
+}
+
+static void
+finish_message_delete_with_rsvp (EMailPartItip *pitip,
+ ItipView *view,
+ ECalClient *client)
+{
+ gboolean save_schedules = e_cal_client_check_save_schedules (client);
+
+ if (!save_schedules && pitip->delete_message && pitip->folder)
+ camel_folder_delete_message (pitip->folder, pitip->uid);
+
+ if (itip_view_get_rsvp (view)) {
+ ECalComponent *comp = NULL;
+ icalcomponent *ical_comp;
+ icalproperty *prop;
+ icalvalue *value;
+ const gchar *attendee;
+ gchar *comment;
+ GSList *l, *list = NULL;
+ gboolean found;
+
+ comp = e_cal_component_clone (pitip->comp);
+ if (comp == NULL)
+ return;
+
+ if (pitip->to_address == NULL)
+ find_to_address (pitip, pitip->ical_comp, NULL);
+ g_assert (pitip->to_address != NULL);
+
+ ical_comp = e_cal_component_get_icalcomponent (comp);
+
+ /* Remove all attendees except the one we are responding as */
+ found = FALSE;
+ for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
+ prop != NULL;
+ prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
+ gchar *text;
+
+ value = icalproperty_get_value (prop);
+ if (!value)
+ continue;
+
+ attendee = icalvalue_get_string (value);
+
+ text = g_strdup (itip_strip_mailto (attendee));
+ text = g_strstrip (text);
+
+ /* We do this to ensure there is at most one
+ * attendee in the response */
+ if (found || g_ascii_strcasecmp (pitip->to_address, text))
+ list = g_slist_prepend (list, prop);
+ else if (!g_ascii_strcasecmp (pitip->to_address, text))
+ found = TRUE;
+ g_free (text);
+ }
+
+ for (l = list; l; l = l->next) {
+ prop = l->data;
+ icalcomponent_remove_property (ical_comp, prop);
+ icalproperty_free (prop);
+ }
+ g_slist_free (list);
+
+ /* Add a comment if there user set one */
+ comment = itip_view_get_rsvp_comment (view);
+ if (comment) {
+ GSList comments;
+ ECalComponentText text;
+
+ text.value = comment;
+ text.altrep = NULL;
+
+ comments.data = &text;
+ comments.next = NULL;
+
+ e_cal_component_set_comment_list (comp, &comments);
+
+ g_free (comment);
+ }
+
+ e_cal_component_rescan (comp);
+ if (itip_send_comp (
+ pitip->registry,
+ E_CAL_COMPONENT_METHOD_REPLY,
+ comp, pitip->current_client,
+ pitip->top_level, NULL, NULL, TRUE, FALSE) &&
+ pitip->folder) {
+ camel_folder_set_message_flags (
+ pitip->folder, pitip->uid,
+ CAMEL_MESSAGE_ANSWERED,
+ CAMEL_MESSAGE_ANSWERED);
+ }
+
+ g_object_unref (comp);
+ }
+
+ update_item_progress_info (pitip, view, NULL);
+}
+
+static void
+receive_objects_ready_cb (GObject *ecalclient,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *client = E_CAL_CLIENT (ecalclient);
+ ESource *source = e_client_get_source (E_CLIENT (client));
+ ItipView *view = user_data;
+ EMailPartItip *pitip = itip_view_get_mail_part (view);
+ gboolean save_schedules;
+ GError *error = NULL;
+
+ if (!e_cal_client_receive_objects_finish (client, result, &error)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+ !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
+ update_item_progress_info (pitip, view, NULL);
+ pitip->update_item_error_info_id =
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Unable to send item to calendar '%s'. %s"),
+ e_source_get_display_name (source), error ? error->message : _("Unknown error"));
+ }
+ g_clear_error (&error);
+ return;
+ }
+
+ itip_view_set_extension_name (view, NULL);
+
+ itip_view_clear_lower_info_items (view);
+
+ switch (pitip->update_item_response) {
+ case ITIP_VIEW_RESPONSE_ACCEPT:
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Sent to calendar '%s' as accepted"), e_source_get_display_name (source));
+ break;
+ case ITIP_VIEW_RESPONSE_TENTATIVE:
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Sent to calendar '%s' as tentative"), e_source_get_display_name (source));
+ break;
+ case ITIP_VIEW_RESPONSE_DECLINE:
+ /* FIXME some calendars just might not save it at all, is this accurate? */
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Sent to calendar '%s' as declined"), e_source_get_display_name (source));
+ break;
+ case ITIP_VIEW_RESPONSE_CANCEL:
+ /* FIXME some calendars just might not save it at all, is this accurate? */
+ itip_view_add_lower_info_item_printf (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Sent to calendar '%s' as canceled"), e_source_get_display_name (source));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /*FIXME Save schedules is misused here, remove it */
+ save_schedules = e_cal_client_check_save_schedules (client);
+
+ /* FIXME Remove this and handle this at the groupwise mail provider */
+ if (save_schedules && pitip->can_delete_invitation_from_cache && pitip->folder) {
+ CamelFolderChangeInfo *changes = NULL;
+ const gchar *tag = NULL;
+ CamelMessageInfo *mi;
+ mi = camel_folder_summary_get (pitip->folder->summary, pitip->uid);
+ if (mi) {
+ changes = camel_folder_change_info_new ();
+
+ if (itip_view_get_recur_check_state (view)) {
+ /* Recurring appointment and "apply-to-all" is selected */
+ tag = camel_message_info_user_tag (mi, "recurrence-key");
+ if (tag) {
+ gint i;
+ GPtrArray *known_uids;
+
+ known_uids = camel_folder_summary_get_array (pitip->folder->summary);
+ for (i = 0; known_uids && i < known_uids->len; i++) {
+ const gchar *uid = g_ptr_array_index (known_uids, i);
+ CamelMessageInfo *mi2;
+
+ mi2 = camel_folder_summary_get (pitip->folder->summary, uid);
+ if (!mi2)
+ continue;
+
+ if (camel_message_info_user_tag (mi2, "recurrence-key") &&
+ g_str_equal (camel_message_info_user_tag (mi2, "recurrence-key"), tag)) {
+ camel_folder_summary_remove_uid (pitip->folder->summary, mi2->uid);
+ camel_folder_change_info_remove_uid (changes, mi2->uid);
+ }
+
+ camel_message_info_free (mi2);
+ }
+ }
+ } else {
+ /* Either not a recurring appointment or "apply-to-all" is not selected. So just delete this instance alone */
+ camel_folder_summary_remove_uid (pitip->folder->summary, pitip->uid);
+ camel_folder_change_info_remove_uid (changes, pitip->uid);
+ }
+
+ camel_folder_changed (pitip->folder, changes);
+ camel_folder_change_info_free (changes);
+ camel_message_info_free (mi);
+ }
+ }
+
+ finish_message_delete_with_rsvp (pitip, view, client);
+}
+
+static void
+update_item (EMailPartItip *pitip,
+ ItipView *view,
+ ItipViewResponse response)
+{
+ struct icaltimetype stamp;
+ icalproperty *prop;
+ icalcomponent *clone;
+ ECalComponent *clone_comp;
+ gchar *str;
+
+ update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
+
+ /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
+ * the user accepted/declined the request. (Outlook ignores
+ * SEQUENCE in REPLY reponses and instead requires that each
+ * updated response have a later REPLYTIME than the previous
+ * one.) This also ends up getting saved in our own copy of
+ * the meeting, though there's currently no way to see that
+ * information (unless it's being saved to an Exchange folder
+ * and you then look at it in Outlook).
+ */
+ stamp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
+ str = icaltime_as_ical_string_r (stamp);
+ prop = icalproperty_new_x (str);
+ g_free (str);
+ icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME");
+ icalcomponent_add_property (pitip->ical_comp, prop);
+
+ clone = icalcomponent_new_clone (pitip->ical_comp);
+ icalcomponent_add_component (pitip->top_level, clone);
+ icalcomponent_set_method (pitip->top_level, pitip->method);
+
+ if (!itip_view_get_inherit_alarm_check_state (view)) {
+ icalcomponent *alarm_comp;
+ icalcompiter alarm_iter;
+
+ alarm_iter = icalcomponent_begin_component (clone, ICAL_VALARM_COMPONENT);
+ while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
+ icalcompiter_next (&alarm_iter);
+
+ icalcomponent_remove_component (clone, alarm_comp);
+ icalcomponent_free (alarm_comp);
+ }
+ }
+
+ clone_comp = e_cal_component_new ();
+ if (!e_cal_component_set_icalcomponent (clone_comp, clone)) {
+ update_item_progress_info (pitip, view, NULL);
+ pitip->update_item_error_info_id = itip_view_add_lower_info_item (view,
+ ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to parse item"));
+ goto cleanup;
+ }
+
+ if (itip_view_get_keep_alarm_check_state (view)) {
+ ECalComponent *real_comp;
+ GList *alarms, *l;
+ ECalComponentAlarm *alarm;
+
+ real_comp = get_real_item (pitip);
+ if (real_comp != NULL) {
+ alarms = e_cal_component_get_alarm_uids (real_comp);
+
+ for (l = alarms; l; l = l->next) {
+ alarm = e_cal_component_get_alarm (real_comp, (const gchar *) l->data);
+
+ if (alarm) {
+ ECalComponentAlarm *aclone = e_cal_component_alarm_clone (alarm);
+
+ if (aclone) {
+ e_cal_component_add_alarm (clone_comp, aclone);
+ e_cal_component_alarm_free (aclone);
+ }
+
+ e_cal_component_alarm_free (alarm);
+ }
+ }
+
+ cal_obj_uid_list_free (alarms);
+ g_object_unref (real_comp);
+ }
+ }
+
+ if ((response != ITIP_VIEW_RESPONSE_CANCEL)
+ && (response != ITIP_VIEW_RESPONSE_DECLINE)) {
+ GSList *attachments = NULL, *new_attachments = NULL, *l;
+ CamelMimeMessage *msg = pitip->msg;
+
+ e_cal_component_get_attachment_list (clone_comp, &attachments);
+
+ for (l = attachments; l; l = l->next) {
+ GSList *parts = NULL, *m;
+ gchar *uri, *new_uri;
+ CamelMimePart *part;
+
+ uri = l->data;
+
+ if (!g_ascii_strncasecmp (uri, "cid:...", 7)) {
+ message_foreach_part ((CamelMimePart *) msg, &parts);
+
+ for (m = parts; m; m = m->next) {
+ part = m->data;
+
+ /* Skip the actual message and the text/calendar part */
+ /* FIXME Do we need to skip anything else? */
+ if (part == (CamelMimePart *) msg || part == pitip->part)
+ continue;
+
+ new_uri = get_uri_for_part (part);
+ if (new_uri != NULL)
+ new_attachments = g_slist_append (new_attachments, new_uri);
+ }
+
+ g_slist_free (parts);
+
+ } else if (!g_ascii_strncasecmp (uri, "cid:", 4)) {
+ part = camel_mime_message_get_part_by_content_id (msg, uri + 4);
+ if (part) {
+ new_uri = get_uri_for_part (part);
+ if (new_uri != NULL)
+ new_attachments = g_slist_append (new_attachments, new_uri);
+ }
+
+ } else {
+ /* Preserve existing non-cid ones */
+ new_attachments = g_slist_append (new_attachments, g_strdup (uri));
+ }
+ }
+
+ g_slist_foreach (attachments, (GFunc) g_free, NULL);
+ g_slist_free (attachments);
+
+ e_cal_component_set_attachment_list (clone_comp, new_attachments);
+ }
+
+ pitip->update_item_response = response;
+
+ e_cal_client_receive_objects (
+ pitip->current_client,
+ pitip->top_level,
+ pitip->cancellable,
+ receive_objects_ready_cb,
+ view);
+
+ cleanup:
+ icalcomponent_remove_component (pitip->top_level, clone);
+ g_object_unref (clone_comp);
+}
+
+/* TODO These operations should be available in e-cal-component.c */
+static void
+set_attendee (ECalComponent *comp,
+ const gchar *address)
+{
+ icalproperty *prop;
+ icalcomponent *icalcomp;
+ gboolean found = FALSE;
+
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+
+ for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
+ prop;
+ prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
+ const gchar *attendee = icalproperty_get_attendee (prop);
+
+ if (!(g_str_equal (itip_strip_mailto (attendee), address)))
+ icalcomponent_remove_property (icalcomp, prop);
+ else
+ found = TRUE;
+ }
+
+ if (!found) {
+ icalparameter *param;
+ gchar *temp = g_strdup_printf ("MAILTO:%s", address);
+
+ prop = icalproperty_new_attendee ((const gchar *) temp);
+ icalcomponent_add_property (icalcomp, prop);
+
+ param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION);
+ icalproperty_add_parameter (prop, param);
+
+ param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
+ icalproperty_add_parameter (prop, param);
+
+ param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
+ icalproperty_add_parameter (prop, param);
+
+ param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
+ icalproperty_add_parameter (prop, param);
+
+ g_free (temp);
+ }
+
+}
+
+static gboolean
+send_comp_to_attendee (ESourceRegistry *registry,
+ ECalComponentItipMethod method,
+ ECalComponent *comp,
+ const gchar *user,
+ ECalClient *client,
+ const gchar *comment)
+{
+ gboolean status;
+ ECalComponent *send_comp = e_cal_component_clone (comp);
+
+ set_attendee (send_comp, user);
+
+ if (comment) {
+ GSList comments;
+ ECalComponentText text;
+
+ text.value = comment;
+ text.altrep = NULL;
+
+ comments.data = &text;
+ comments.next = NULL;
+
+ e_cal_component_set_comment_list (send_comp, &comments);
+ }
+
+ /* FIXME send the attachments in the request */
+ status = itip_send_comp (
+ registry, method, send_comp,
+ client, NULL, NULL, NULL, TRUE, FALSE);
+
+ g_object_unref (send_comp);
+
+ return status;
+}
+
+static void
+remove_delegate (EMailPartItip *pitip,
+ ItipView *view,
+ const gchar *delegate,
+ const gchar *delegator,
+ ECalComponent *comp)
+{
+ gboolean status;
+ gchar *comment = g_strdup_printf (_("Organizer has removed the delegate %s "), itip_strip_mailto (delegate));
+
+ /* send cancellation notice to delegate */
+ status = send_comp_to_attendee (
+ pitip->registry,
+ E_CAL_COMPONENT_METHOD_CANCEL, pitip->comp,
+ delegate, pitip->current_client, comment);
+ if (status)
+ send_comp_to_attendee (
+ pitip->registry,
+ E_CAL_COMPONENT_METHOD_REQUEST, pitip->comp,
+ delegator, pitip->current_client, comment);
+ if (status) {
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Sent a cancelation notice to the delegate"));
+ } else
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Could not send the cancelation notice to the delegate"));
+
+ g_free (comment);
+
+}
+
+static void
+update_x (ECalComponent *pitip_comp,
+ ECalComponent *comp)
+{
+ icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (pitip_comp);
+ icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
+
+ icalproperty *prop = icalcomponent_get_first_property (itip_icalcomp, ICAL_X_PROPERTY);
+ while (prop) {
+ const gchar *name = icalproperty_get_x_name (prop);
+ if (!g_ascii_strcasecmp (name, "X-EVOLUTION-IS-REPLY")) {
+ icalproperty *new_prop = icalproperty_new_x (icalproperty_get_x (prop));
+ icalproperty_set_x_name (new_prop, "X-EVOLUTION-IS-REPLY");
+ icalcomponent_add_property (icalcomp, new_prop);
+ }
+ prop = icalcomponent_get_next_property (itip_icalcomp, ICAL_X_PROPERTY);
+ }
+
+ e_cal_component_set_icalcomponent (comp, icalcomp);
+}
+
+static void
+modify_object_cb (GObject *ecalclient,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *client = E_CAL_CLIENT (ecalclient);
+ ItipView *view = user_data;
+ EMailPartItip *pitip = itip_view_get_mail_part (view);
+ GError *error = NULL;
+
+ if (!e_cal_client_modify_object_finish (client, result, &error)) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
+ g_clear_error (&error);
+ return;
+ }
+
+ update_item_progress_info (pitip, view, NULL);
+ pitip->update_item_error_info_id =
+ itip_view_add_lower_info_item_printf (
+ view,
+ ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
+ _("Unable to update attendee. %s"),
+ error ? error->message : _("Unknown error"));
+
+ g_clear_error (&error);
+ } else {
+ update_item_progress_info (pitip, view, NULL);
+ itip_view_add_lower_info_item (view,
+ ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("Attendee status updated"));
+ }
+}
+
+static void
+update_attendee_status_icalcomp (EMailPartItip *pitip,
+ ItipView *view,
+ icalcomponent *icalcomp)
+{
+ ECalComponent *comp;
+ const gchar *uid = NULL;
+ gchar *rid;
+ GSList *attendees;
+
+ e_cal_component_get_uid (pitip->comp, &uid);
+ rid = e_cal_component_get_recurid_as_string (pitip->comp);
+
+ comp = e_cal_component_new ();
+ if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
+ icalcomponent_free (icalcomp);
+
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
+ _("The meeting is invalid and cannot be updated"));
+ } else {
+ icalcomponent *org_icalcomp;
+ const gchar *delegate;
+
+ org_icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
+
+ e_cal_component_get_attendee_list (pitip->comp, &attendees);
+ if (attendees != NULL) {
+ ECalComponentAttendee *a = attendees->data;
+ icalproperty *prop, *del_prop;
+ EShell *shell = e_shell_get_default ();
+
+ prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
+ if ((a->status == ICAL_PARTSTAT_DELEGATED) && (del_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->delto))) && !(find_attendee (icalcomp, itip_strip_mailto (a->delto)))) {
+ gint response;
+ delegate = icalproperty_get_attendee (del_prop);
+ response = e_alert_run_dialog_for_args (e_shell_get_active_window (shell),
+ "org.gnome.itip-formatter:add-delegate",
+ itip_strip_mailto (a->value),
+ itip_strip_mailto (delegate), NULL);
+ if (response == GTK_RESPONSE_YES) {
+ icalcomponent_add_property (icalcomp, icalproperty_new_clone (del_prop));
+ e_cal_component_rescan (comp);
+ } else if (response == GTK_RESPONSE_NO) {
+ remove_delegate (pitip, view, delegate, itip_strip_mailto (a->value), comp);
+ goto cleanup;
+ } else {
+ goto cleanup;
+ }
+ }
+
+ if (prop == NULL) {
+ gint response;
+
+ if (a->delfrom && *a->delfrom) {
+ response = e_alert_run_dialog_for_args (e_shell_get_active_window (shell),
+ "org.gnome.itip-formatter:add-delegate",
+ itip_strip_mailto (a->delfrom),
+ itip_strip_mailto (a->value), NULL);
+ if (response == GTK_RESPONSE_YES) {
+ /* Already declared in this function */
+ icalproperty *prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
+ icalcomponent_add_property (icalcomp,icalproperty_new_clone (prop));
+ e_cal_component_rescan (comp);
+ } else if (response == GTK_RESPONSE_NO) {
+ remove_delegate (pitip,
+ view,
+ itip_strip_mailto (a->value),
+ itip_strip_mailto (a->delfrom),
+ comp);
+ goto cleanup;
+ } else {
+ goto cleanup;
+ }
+ }
+
+ response = e_alert_run_dialog_for_args (
+ e_shell_get_active_window (shell),
+ "org.gnome.itip-formatter:add-unknown-attendee", NULL);
+
+ if (response == GTK_RESPONSE_YES) {
+ change_status (
+ pitip->registry, icalcomp,
+ itip_strip_mailto (a->value),
+ a->status);
+ e_cal_component_rescan (comp);
+ } else {
+ goto cleanup;
+ }
+ } else if (a->status == ICAL_PARTSTAT_NONE || a->status == ICAL_PARTSTAT_X) {
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
+ _("Attendee status could not be updated because the status is invalid"));
+ goto cleanup;
+ } else {
+ if (a->status == ICAL_PARTSTAT_DELEGATED) {
+ /* *prop already declared in this function */
+ icalproperty *prop, *new_prop;
+
+ prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
+ icalcomponent_remove_property (icalcomp, prop);
+
+ new_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->value));
+ icalcomponent_add_property (icalcomp, icalproperty_new_clone (new_prop));
+ } else {
+ change_status (
+ pitip->registry, icalcomp,
+ itip_strip_mailto (a->value),
+ a->status);
+ }
+
+ e_cal_component_rescan (comp);
+ }
+ }
+ }
+
+ update_x (pitip->comp, comp);
+
+ if (itip_view_get_update (view)) {
+ e_cal_component_commit_sequence (comp);
+ itip_send_comp (
+ pitip->registry,
+ E_CAL_COMPONENT_METHOD_REQUEST,
+ comp, pitip->current_client,
+ NULL, NULL, NULL, TRUE, FALSE);
+ }
+
+ update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
+
+ e_cal_client_modify_object (
+ pitip->current_client,
+ icalcomp, rid ? CALOBJ_MOD_THIS : CALOBJ_MOD_ALL,
+ pitip->cancellable,
+ modify_object_cb,
+ view);
+
+ cleanup:
+ g_object_unref (comp);
+}
+
+static void
+update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *client = E_CAL_CLIENT (ecalclient);
+ ItipView *view = user_data;
+ EMailPartItip *pitip = itip_view_get_mail_part (view);
+ icalcomponent *icalcomp = NULL;
+ GError *error = NULL;
+
+ if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
+ g_clear_error (&error);
+ return;
+ }
+
+ g_clear_error (&error);
+
+ update_item_progress_info (pitip, view, NULL);
+ pitip->update_item_error_info_id = itip_view_add_lower_info_item (
+ view,
+ ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
+ _("Attendee status can not be updated because the item no longer exists"));
+ return;
+ }
+
+ update_attendee_status_icalcomp (pitip, view, icalcomp);
+}
+
+static void
+update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECalClient *client = E_CAL_CLIENT (ecalclient);
+ ItipView *view = user_data;
+ EMailPartItip *pitip = itip_view_get_mail_part (view);
+ icalcomponent *icalcomp = NULL;
+ GError *error = NULL;
+
+ if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
+ const gchar *uid;
+ gchar *rid;
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
+ g_clear_error (&error);
+ return;
+ }
+
+ g_clear_error (&error);
+
+ e_cal_component_get_uid (pitip->comp, &uid);
+ rid = e_cal_component_get_recurid_as_string (pitip->comp);
+
+ if (!rid || !*rid) {
+ g_free (rid);
+
+ update_item_progress_info (pitip, view, NULL);
+ pitip->update_item_error_info_id = itip_view_add_lower_info_item (
+ view,
+ ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
+ _("Attendee status can not be updated because the item no longer exists"));
+ return;
+ }
+
+ e_cal_client_get_object (
+ pitip->current_client,
+ uid,
+ NULL,
+ pitip->cancellable,
+ update_attendee_status_get_object_without_rid_cb,
+ view);
+
+ g_free (rid);
+ return;
+ }
+
+ update_attendee_status_icalcomp (pitip, view, icalcomp);
+}
+
+static void
+update_attendee_status (EMailPartItip *pitip,
+ ItipView *view)
+{
+ const gchar *uid = NULL;
+ gchar *rid;
+
+ /* Obtain our version */
+ e_cal_component_get_uid (pitip->comp, &uid);
+ rid = e_cal_component_get_recurid_as_string (pitip->comp);
+
+ update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
+
+ /* search for a master object if the detached object doesn't exist in the calendar */
+ e_cal_client_get_object (
+ pitip->current_client,
+ uid, rid,
+ pitip->cancellable,
+ update_attendee_status_get_object_with_rid_cb,
+ view);
+
+ g_free (rid);
+}
+
+static void
+send_item (EMailPartItip *pitip,
+ ItipView *view)
+{
+ ECalComponent *comp;
+
+ comp = get_real_item (pitip);
+
+ if (comp != NULL) {
+ itip_send_comp (
+ pitip->registry,
+ E_CAL_COMPONENT_METHOD_REQUEST,
+ comp, pitip->current_client,
+ NULL, NULL, NULL, TRUE, FALSE);
+ g_object_unref (comp);
+
+ switch (pitip->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Meeting information sent"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Task information sent"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
+ _("Memo information sent"));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ } else {
+ switch (pitip->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
+ _("Unable to send meeting information, the meeting does not exist"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
+ _("Unable to send task information, the task does not exist"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ itip_view_add_lower_info_item (
+ view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
+ _("Unable to send memo information, the memo does not exist"));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+}
+
+static icalcomponent *
+get_next (icalcompiter *iter)
+{
+ icalcomponent *ret = NULL;
+ icalcomponent_kind kind;
+
+ do {
+ icalcompiter_next (iter);
+ ret = icalcompiter_deref (iter);
+ if (ret == NULL)
+ break;
+ kind = icalcomponent_isa (ret);
+ } while (ret != NULL
+ && kind != ICAL_VEVENT_COMPONENT
+ && kind != ICAL_VTODO_COMPONENT
+ && kind != ICAL_VFREEBUSY_COMPONENT);
+
+ return ret;
+}
+
+static void
+attachment_load_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GFile *file)
+{
+ EShell *shell;
+ GtkWindow *parent;
+
+ /* XXX Theoretically, this should never fail. */
+ e_attachment_load_finish (attachment, result, NULL);
+
+ shell = e_shell_get_default ();
+ parent = e_shell_get_active_window (shell);
+
+ e_attachment_save_async (
+ attachment, file, (GAsyncReadyCallback)
+ e_attachment_save_handle_error, parent);
+
+ g_object_unref (file);
+}
+
+static void
+save_vcalendar_cb (EMailPartItip *pitip)
+{
+ EAttachment *attachment;
+ EShell *shell;
+ GFile *file;
+ const gchar *suggestion;
+
+ g_return_if_fail (pitip != NULL);
+ g_return_if_fail (pitip->vcalendar != NULL);
+ g_return_if_fail (pitip->part != NULL);
+
+ suggestion = camel_mime_part_get_filename (pitip->part);
+ if (suggestion == NULL) {
+ /* Translators: This is a default filename for a calendar. */
+ suggestion = _("calendar.ics");
+ }
+
+ shell = e_shell_get_default ();
+ file = e_shell_run_save_dialog (
+ shell, _("Save Calendar"), suggestion, "*.ics:text/calendar", NULL, NULL);
+ if (file == NULL)
+ return;
+
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, pitip->part);
+
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ attachment_load_finish, file);
+}
+
+static void
+set_itip_error (ItipView *view,
+ const gchar *primary,
+ const gchar *secondary,
+ gboolean save_btn)
+{
+ gchar *error;
+
+ error = g_strdup_printf (
+ "<div class=\"error\">"
+ "<p><b>%s</b></p>"
+ "<p>%s</p>",
+ primary, secondary);
+
+ itip_view_set_error (view, error, save_btn);
+
+ g_free (error);
+}
+
+static gboolean
+extract_itip_data (EMailPartItip *pitip,
+ ItipView *view,
+ gboolean *have_alarms)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ icalproperty *prop;
+ icalcomponent_kind kind = ICAL_NO_COMPONENT;
+ icalcomponent *tz_comp;
+ icalcompiter tz_iter;
+ icalcomponent *alarm_comp;
+ icalcompiter alarm_iter;
+ ECalComponent *comp;
+ gboolean use_default_reminder;
+
+ shell = e_shell_get_default ();
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ if (!pitip->vcalendar) {
+ set_itip_error (view,
+ _("The calendar attached is not valid"),
+ _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
+ FALSE);
+
+ return FALSE;
+ }
+
+ pitip->top_level = e_cal_util_new_top_level ();
+
+ pitip->main_comp = icalparser_parse_string (pitip->vcalendar);
+ if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) {
+ set_itip_error (view,
+ _("The calendar attached is not valid"),
+ _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
+ FALSE);
+
+ if (pitip->main_comp) {
+ icalcomponent_free (pitip->main_comp);
+ pitip->main_comp = NULL;
+ }
+
+ return FALSE;
+ }
+
+ prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY);
+ if (prop == NULL) {
+ pitip->method = ICAL_METHOD_PUBLISH;
+ } else {
+ pitip->method = icalproperty_get_method (prop);
+ }
+
+ tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT);
+ while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) {
+ icalcomponent *clone;
+
+ clone = icalcomponent_new_clone (tz_comp);
+ icalcomponent_add_component (pitip->top_level, clone);
+
+ icalcompiter_next (&tz_iter);
+ }
+
+ pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT);
+ pitip->ical_comp = icalcompiter_deref (&pitip->iter);
+ if (pitip->ical_comp != NULL) {
+ kind = icalcomponent_isa (pitip->ical_comp);
+ if (kind != ICAL_VEVENT_COMPONENT
+ && kind != ICAL_VTODO_COMPONENT
+ && kind != ICAL_VFREEBUSY_COMPONENT
+ && kind != ICAL_VJOURNAL_COMPONENT)
+ pitip->ical_comp = get_next (&pitip->iter);
+ }
+
+ if (pitip->ical_comp == NULL) {
+ set_itip_error (view,
+ _("The item in the calendar is not valid"),
+ _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
+ FALSE);
+
+ return FALSE;
+ }
+
+ switch (icalcomponent_isa (pitip->ical_comp)) {
+ case ICAL_VEVENT_COMPONENT:
+ pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
+ pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL;
+ if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) {
+ /* no attendees: assume that that this is not a meeting and organizer doesn't want a reply */
+ pitip->no_reply_wanted = TRUE;
+ } else {
+ /*
+ * if we have attendees, then find_to_address() will check for our RSVP
+ * and set no_reply_wanted=TRUE if RSVP=FALSE for the current user
+ */
+ }
+ break;
+ case ICAL_VTODO_COMPONENT:
+ pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
+ break;
+ case ICAL_VJOURNAL_COMPONENT:
+ pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
+ break;
+ default:
+ set_itip_error (view,
+ _("The item in the calendar is not valid"),
+ _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
+ FALSE);
+
+ return FALSE;
+ }
+
+ pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT);
+ pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT);
+ pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT);
+ pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT);
+
+ if (pitip->total > 1) {
+
+ set_itip_error (view,
+ _("The calendar attached contains multiple items"),
+ _("To process all of these items, the file should be saved and the calendar imported"),
+ TRUE);
+
+ } if (pitip->total > 0) {
+ pitip->current = 1;
+ } else {
+ pitip->current = 0;
+ }
+
+ if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) {
+ gchar *my_address;
+
+ prop = NULL;
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp));
+ my_address = itip_get_comp_attendee (pitip->registry, comp, NULL);
+ g_object_unref (comp);
+ comp = NULL;
+
+ if (!prop)
+ prop = find_attendee (pitip->ical_comp, my_address);
+ if (!prop)
+ prop = find_attendee_if_sentby (pitip->ical_comp, my_address);
+ if (prop) {
+ icalparameter *param;
+ const gchar * delfrom;
+
+ if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) {
+ delfrom = icalparameter_get_delegatedfrom (param);
+
+ pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom));
+ }
+ }
+ g_free (my_address);
+ prop = NULL;
+
+ /* Determine any delegate sections */
+ prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY);
+ while (prop) {
+ const gchar *x_name, *x_val;
+
+ x_name = icalproperty_get_x_name (prop);
+ x_val = icalproperty_get_x (prop);
+
+ if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID"))
+ pitip->calendar_uid = g_strdup (x_val);
+ else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI"))
+ g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used");
+ else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS"))
+ pitip->delegator_address = g_strdup (x_val);
+ else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME"))
+ pitip->delegator_name = g_strdup (x_val);
+
+ prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY);
+ }
+
+ /* Strip out procedural alarms for security purposes */
+ alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
+ while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
+ icalproperty *p;
+
+ icalcompiter_next (&alarm_iter);
+
+ p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY);
+ if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE)
+ icalcomponent_remove_component (pitip->ical_comp, alarm_comp);
+
+ icalcomponent_free (alarm_comp);
+ }
+
+ if (have_alarms) {
+ alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
+ *have_alarms = icalcompiter_deref (&alarm_iter) != NULL;
+ }
+ }
+
+ pitip->comp = e_cal_component_new ();
+ if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) {
+ g_object_unref (pitip->comp);
+ pitip->comp = NULL;
+
+ set_itip_error (view,
+ _("The item in the calendar is not valid"),
+ _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
+ FALSE);
+
+ return FALSE;
+ };
+
+ /* Add default reminder if the config says so */
+
+ use_default_reminder = e_shell_settings_get_boolean (
+ shell_settings, "cal-use-default-reminder");
+
+ if (use_default_reminder) {
+ ECalComponentAlarm *acomp;
+ gint interval;
+ EDurationType units;
+ ECalComponentAlarmTrigger trigger;
+
+ interval = e_shell_settings_get_int (
+ shell_settings, "cal-default-reminder-interval");
+ units = e_shell_settings_get_int (
+ shell_settings, "cal-default-reminder-units");
+
+ acomp = e_cal_component_alarm_new ();
+
+ e_cal_component_alarm_set_action (acomp, E_CAL_COMPONENT_ALARM_DISPLAY);
+
+ trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+ memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration));
+
+ trigger.u.rel_duration.is_neg = TRUE;
+
+ switch (units) {
+ case E_DURATION_MINUTES:
+ trigger.u.rel_duration.minutes = interval;
+ break;
+ case E_DURATION_HOURS:
+ trigger.u.rel_duration.hours = interval;
+ break;
+ case E_DURATION_DAYS:
+ trigger.u.rel_duration.days = interval;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ e_cal_component_alarm_set_trigger (acomp, trigger);
+ e_cal_component_add_alarm (pitip->comp, acomp);
+
+ e_cal_component_alarm_free (acomp);
+ }
+
+ find_from_address (pitip, pitip->ical_comp);
+ find_to_address (pitip, pitip->ical_comp, NULL);
+
+ return TRUE;
+}
+
+struct _opencal_msg {
+ MailMsg base;
+
+ gchar *command; /* command line to run */
+};
+
+static gchar *
+open_calendar__desc (struct _opencal_msg *m,
+ gint complete)
+{
+ return g_strdup (_("Opening calendar"));
+}
+
+static void
+open_calendar__exec (struct _opencal_msg *m,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (!g_spawn_command_line_async (m->command, NULL)) {
+ g_warning ("Could not launch %s", m->command);
+ }
+}
+
+static void
+open_calendar__free (struct _opencal_msg *m)
+{
+ g_free (m->command);
+ m->command = NULL;
+}
+
+static MailMsgInfo open_calendar_info = {
+ sizeof (struct _opencal_msg),
+ (MailMsgDescFunc) open_calendar__desc,
+ (MailMsgExecFunc) open_calendar__exec,
+ (MailMsgDoneFunc) NULL,
+ (MailMsgFreeFunc) open_calendar__free,
+};
+
+static gboolean
+idle_open_cb (gpointer data)
+{
+ EMailPartItip *pitip = data;
+ struct _opencal_msg *m;
+ gchar *start, *end;
+
+ start = isodate_from_time_t (pitip->start_time);
+ end = isodate_from_time_t (pitip->end_time);
+ m = mail_msg_new (&open_calendar_info);
+ m->command = g_strdup_printf ("evolution \"calendar:///?startdate=%s&enddate=%s\"", start, end);
+ mail_msg_slow_ordered_push (m);
+
+ g_free (start);
+ g_free (end);
+
+ return FALSE;
+}
+
+static void
+view_response_cb (ItipView *view,
+ ItipViewResponse response,
+ gpointer data)
+{
+ EMailPartItip *pitip = data;
+ gboolean status = FALSE;
+ icalproperty *prop;
+ ECalComponentTransparency trans;
+
+ if (response == ITIP_VIEW_RESPONSE_SAVE) {
+ save_vcalendar_cb (pitip);
+ return;
+ }
+
+ pitip->can_delete_invitation_from_cache = FALSE;
+ if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) {
+ if (itip_view_get_free_time_check_state (view))
+ e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
+ else
+ e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
+ } else {
+ e_cal_component_get_transparency (pitip->comp, &trans);
+
+ if (trans == E_CAL_COMPONENT_TRANSP_NONE)
+ e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
+ }
+
+ if (!pitip->to_address && pitip->current_client != NULL)
+ e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL);
+
+ /* check if it is a recur instance (no master object) and
+ * add a property */
+ if (itip_view_get_recur_check_state (view)) {
+ prop = icalproperty_new_x ("All");
+ icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE");
+ icalcomponent_add_property (pitip->ical_comp, prop);
+ }
+
+ switch (response) {
+ case ITIP_VIEW_RESPONSE_ACCEPT:
+ if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
+ status = change_status (
+ pitip->registry,
+ pitip->ical_comp,
+ pitip->to_address,
+ ICAL_PARTSTAT_ACCEPTED);
+ else
+ status = TRUE;
+ if (status) {
+ e_cal_component_rescan (pitip->comp);
+ pitip->can_delete_invitation_from_cache = TRUE;
+ update_item (pitip, view, response);
+ }
+ break;
+ case ITIP_VIEW_RESPONSE_TENTATIVE:
+ status = change_status (
+ pitip->registry,
+ pitip->ical_comp,
+ pitip->to_address,
+ ICAL_PARTSTAT_TENTATIVE);
+ if (status) {
+ e_cal_component_rescan (pitip->comp);
+ pitip->can_delete_invitation_from_cache = TRUE;
+ update_item (pitip, view, response);
+ }
+ break;
+ case ITIP_VIEW_RESPONSE_DECLINE:
+ if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
+ status = change_status (
+ pitip->registry,
+ pitip->ical_comp,
+ pitip->to_address,
+ ICAL_PARTSTAT_DECLINED);
+ else {
+ prop = icalproperty_new_x ("1");
+ icalproperty_set_x_name (prop, "X-GW-DECLINED");
+ icalcomponent_add_property (pitip->ical_comp, prop);
+ status = TRUE;
+ }
+
+ if (status) {
+ e_cal_component_rescan (pitip->comp);
+ pitip->can_delete_invitation_from_cache = TRUE;
+ update_item (pitip, view, response);
+ }
+ break;
+ case ITIP_VIEW_RESPONSE_UPDATE:
+ update_attendee_status (pitip, view);
+ break;
+ case ITIP_VIEW_RESPONSE_CANCEL:
+ update_item (pitip, view, response);
+ break;
+ case ITIP_VIEW_RESPONSE_REFRESH:
+ send_item (pitip, view);
+ break;
+ case ITIP_VIEW_RESPONSE_OPEN:
+ g_idle_add (idle_open_cb, pitip);
+ return;
+ default:
+ break;
+ }
+}
+
+static gboolean
+check_is_instance (icalcomponent *icalcomp)
+{
+ icalproperty *icalprop;
+
+ icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
+ while (icalprop) {
+ const gchar *x_name;
+
+ x_name = icalproperty_get_x_name (icalprop);
+ if (!strcmp (x_name, "X-GW-RECURRENCE-KEY")) {
+ return TRUE;
+ }
+ icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+in_proper_folder (ESourceRegistry *registry,
+ CamelFolder *folder)
+{
+ EShell *shell;
+ EShellBackend *shell_backend;
+ EMailBackend *backend;
+ EMailSession *session;
+ MailFolderCache *folder_cache;
+ gboolean res = TRUE;
+ CamelFolderInfoFlags flags = 0;
+
+ if (!folder)
+ return FALSE;
+
+ shell = e_shell_get_default ();
+ shell_backend = e_shell_get_backend_by_name (shell, "mail");
+ backend = E_MAIL_BACKEND (shell_backend);
+ session = e_mail_backend_get_session (backend);
+ folder_cache = e_mail_session_get_folder_cache (session);
+
+ if (mail_folder_cache_get_folder_info_flags (folder_cache, folder, &flags)) {
+ /* it should be neither trash nor junk folder, */
+ res = ((flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_TRASH &&
+ (flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_JUNK &&
+ /* it can be Inbox */
+ ( (flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX ||
+ /* or any other virtual folder */
+ CAMEL_IS_VEE_FOLDER (folder) ||
+ /* or anything else except of sent, outbox or drafts folder */
+ (!em_utils_folder_is_sent (registry, folder) &&
+ !em_utils_folder_is_outbox (registry, folder) &&
+ !em_utils_folder_is_drafts (registry, folder))
+ ));
+ } else {
+ /* cannot check for Inbox folder here */
+ res = (folder->folder_flags & (CAMEL_FOLDER_IS_TRASH | CAMEL_FOLDER_IS_JUNK)) == 0 && (
+ (CAMEL_IS_VEE_FOLDER (folder)) || (
+ !em_utils_folder_is_sent (registry, folder) &&
+ !em_utils_folder_is_outbox (registry, folder) &&
+ !em_utils_folder_is_drafts (registry, folder)));
+ }
+
+ return res;
+}
+
+void
+itip_view_init_view (ItipView *view)
+{
+ EShell *shell;
+ EShellSettings *shell_settings;
+ ESourceRegistry *registry;
+ ECalComponentText text;
+ ECalComponentOrganizer organizer;
+ ECalComponentDateTime datetime;
+ icaltimezone *from_zone, *to_zone;
+ GString *gstring = NULL;
+ GSList *list, *l;
+ icalcomponent *icalcomp;
+ const gchar *string, *org;
+ gint i;
+ gboolean response_enabled;
+ gboolean have_alarms = FALSE;
+ EMailPartItip *info;
+
+ info = view->priv->itip_part;
+ g_return_if_fail (info != NULL);
+
+ shell = e_shell_get_default ();
+ registry = e_shell_get_registry (shell);
+ shell_settings = e_shell_get_shell_settings (shell);
+
+ info->registry = g_object_ref (registry);
+
+ /* Reset current client before initializing view */
+ info->current_client = NULL;
+
+ /* Initialize the ecal hashes */
+ for (i = 0; i < E_CAL_CLIENT_SOURCE_TYPE_LAST; i++)
+ info->clients[i] = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */
+ if (!extract_itip_data (info, view, &have_alarms))
+ return;
+
+ response_enabled = in_proper_folder (registry, info->folder);
+
+ if (!response_enabled) {
+ itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL);
+ } else {
+ itip_view_set_show_inherit_alarm_check (
+ view,
+ have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST));
+
+ switch (info->method) {
+ case ICAL_METHOD_PUBLISH:
+ case ICAL_METHOD_REQUEST:
+ /*
+ * Treat meeting request (sent by organizer directly) and
+ * published evend (forwarded by organizer or attendee) alike:
+ * if the event has an organizer, then it can be replied to and
+ * we show the "accept/tentative/decline" choice.
+ * Otherwise only show "accept".
+ */
+ itip_view_set_mode (view,
+ info->has_organizer ?
+ ITIP_VIEW_MODE_REQUEST :
+ ITIP_VIEW_MODE_PUBLISH);
+ break;
+ case ICAL_METHOD_REPLY:
+ itip_view_set_mode (view, ITIP_VIEW_MODE_REPLY);
+ break;
+ case ICAL_METHOD_ADD:
+ itip_view_set_mode (view, ITIP_VIEW_MODE_ADD);
+ break;
+ case ICAL_METHOD_CANCEL:
+ itip_view_set_mode (view, ITIP_VIEW_MODE_CANCEL);
+ break;
+ case ICAL_METHOD_REFRESH:
+ itip_view_set_mode (view, ITIP_VIEW_MODE_REFRESH);
+ break;
+ case ICAL_METHOD_COUNTER:
+ itip_view_set_mode (view, ITIP_VIEW_MODE_COUNTER);
+ break;
+ case ICAL_METHOD_DECLINECOUNTER:
+ itip_view_set_mode (view, ITIP_VIEW_MODE_DECLINECOUNTER);
+ break;
+ case ICAL_METHOD_X :
+ /* Handle appointment requests from Microsoft Live. This is
+ * a best-at-hand-now handling. Must be revisited when we have
+ * better access to the source of such meetings */
+ info->method = ICAL_METHOD_REQUEST;
+ itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST);
+ break;
+ default:
+ return;
+ }
+ }
+
+ itip_view_set_item_type (view, info->type);
+
+ if (response_enabled) {
+ switch (info->method) {
+ case ICAL_METHOD_REQUEST:
+ /* FIXME What about the name? */
+ itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : info->delegator_address);
+ case ICAL_METHOD_PUBLISH:
+ case ICAL_METHOD_ADD:
+ case ICAL_METHOD_CANCEL:
+ case ICAL_METHOD_DECLINECOUNTER:
+ itip_view_set_show_update_check (view, FALSE);
+
+ /* An organizer sent this */
+ e_cal_component_get_organizer (info->comp, &organizer);
+ org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value);
+
+ itip_view_set_organizer (view, org);
+ if (organizer.sentby)
+ itip_view_set_organizer_sentby (
+ view, itip_strip_mailto (organizer.sentby));
+
+ if (info->my_address) {
+ if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), info->my_address))
+ && !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), info->my_address))
+ && (info->to_address && g_ascii_strcasecmp (info->to_address, info->my_address)))
+ itip_view_set_proxy (view, info->to_name ? info->to_name : info->to_address);
+ }
+ break;
+ case ICAL_METHOD_REPLY:
+ case ICAL_METHOD_REFRESH:
+ case ICAL_METHOD_COUNTER:
+ itip_view_set_show_update_check (view, TRUE);
+
+ /* An attendee sent this */
+ e_cal_component_get_attendee_list (info->comp, &list);
+ if (list != NULL) {
+ ECalComponentAttendee *attendee;
+
+ attendee = list->data;
+
+ itip_view_set_attendee (view, attendee->cn ? attendee->cn : itip_strip_mailto (attendee->value));
+
+ if (attendee->sentby)
+ itip_view_set_attendee_sentby (view, itip_strip_mailto (attendee->sentby));
+
+ if (info->my_address) {
+ if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), info->my_address))
+ && !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), info->my_address))
+ && (info->from_address && g_ascii_strcasecmp (info->from_address, info->my_address)))
+ itip_view_set_proxy (view, info->from_name ? info->from_name : info->from_address);
+ }
+
+ e_cal_component_free_attendee_list (list);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ e_cal_component_get_summary (info->comp, &text);
+ itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None"));
+
+ e_cal_component_get_location (info->comp, &string);
+ itip_view_set_location (view, string);
+
+ /* Status really only applies for REPLY */
+ if (response_enabled && info->method == ICAL_METHOD_REPLY) {
+ e_cal_component_get_attendee_list (info->comp, &list);
+ if (list != NULL) {
+ ECalComponentAttendee *a = list->data;
+
+ switch (a->status) {
+ case ICAL_PARTSTAT_ACCEPTED:
+ itip_view_set_status (view, _("Accepted"));
+ break;
+ case ICAL_PARTSTAT_TENTATIVE:
+ itip_view_set_status (view, _("Tentatively Accepted"));
+ break;
+ case ICAL_PARTSTAT_DECLINED:
+ itip_view_set_status (view, _("Declined"));
+ break;
+ case ICAL_PARTSTAT_DELEGATED:
+ itip_view_set_status (view, _("Delegated"));
+ break;
+ default:
+ itip_view_set_status (view, _("Unknown"));
+ }
+ }
+ e_cal_component_free_attendee_list (list);
+ }
+
+ if (info->method == ICAL_METHOD_REPLY
+ || info->method == ICAL_METHOD_COUNTER
+ || info->method == ICAL_METHOD_DECLINECOUNTER) {
+ /* FIXME Check spec to see if multiple comments are actually valid */
+ /* Comments for iTIP are limited to one per object */
+ e_cal_component_get_comment_list (info->comp, &list);
+ if (list) {
+ ECalComponentText *text = list->data;
+
+ if (text->value) {
+ gchar *html;
+
+ html = camel_text_to_html (
+ text->value,
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
+ 0);
+
+ itip_view_set_comment (view, html);
+
+ g_free (html);
+ }
+ }
+ e_cal_component_free_text_list (list);
+ }
+
+ e_cal_component_get_description_list (info->comp, &list);
+ for (l = list; l; l = l->next) {
+ ECalComponentText *text = l->data;
+
+ if (!gstring && text->value)
+ gstring = g_string_new (text->value);
+ else if (text->value)
+ g_string_append_printf (gstring, "\n\n%s", text->value);
+ }
+
+ e_cal_component_free_text_list (list);
+
+ if (gstring) {
+ gchar *html;
+
+ html = camel_text_to_html (
+ gstring->str,
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
+ CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
+ CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
+ 0);
+
+ itip_view_set_description (view, html);
+ g_string_free (gstring, TRUE);
+ g_free (html);
+ }
+
+ to_zone = e_shell_settings_get_pointer (shell_settings, "cal-timezone");
+
+ e_cal_component_get_dtstart (info->comp, &datetime);
+ info->start_time = 0;
+ if (datetime.value) {
+ struct tm start_tm;
+
+ /* If the timezone is not in the component, guess the local time */
+ /* Should we guess if the timezone is an olsen name somehow? */
+ if (datetime.value->is_utc)
+ from_zone = icaltimezone_get_utc_timezone ();
+ else if (!datetime.value->is_utc && datetime.tzid)
+ from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
+ else
+ from_zone = NULL;
+
+ start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
+
+ itip_view_set_start (view, &start_tm, datetime.value->is_date);
+ info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
+ }
+
+ icalcomp = e_cal_component_get_icalcomponent (info->comp);
+
+ /* Set the recurrence id */
+ if (check_is_instance (icalcomp) && datetime.value) {
+ ECalComponentRange *recur_id;
+ struct icaltimetype icaltime = icaltime_convert_to_zone (*datetime.value, to_zone);
+
+ recur_id = g_new0 (ECalComponentRange, 1);
+ recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE;
+ recur_id->datetime.value = &icaltime;
+ recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone);
+ e_cal_component_set_recurid (info->comp, recur_id);
+ g_free (recur_id); /* it's ok to call g_free here */
+ }
+ e_cal_component_free_datetime (&datetime);
+
+ e_cal_component_get_dtend (info->comp, &datetime);
+ info->end_time = 0;
+ if (datetime.value) {
+ struct tm end_tm;
+
+ /* If the timezone is not in the component, guess the local time */
+ /* Should we guess if the timezone is an olsen name somehow? */
+ if (datetime.value->is_utc)
+ from_zone = icaltimezone_get_utc_timezone ();
+ else if (!datetime.value->is_utc && datetime.tzid)
+ from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
+ else
+ from_zone = NULL;
+
+ if (datetime.value->is_date) {
+ /* RFC says the DTEND is not inclusive, thus subtract one day
+ * if we have a date */
+
+ icaltime_adjust (datetime.value, -1, 0, 0, 0);
+ }
+
+ end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
+
+ itip_view_set_end (view, &end_tm, datetime.value->is_date);
+ info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
+ }
+ e_cal_component_free_datetime (&datetime);
+
+ /* Recurrence info */
+ /* FIXME Better recurring description */
+ if (e_cal_component_has_recurrences (info->comp)) {
+ /* FIXME Tell the user we don't support recurring tasks */
+ switch (info->type) {
+ case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+ itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This meeting recurs"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+ itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This task recurs"));
+ break;
+ case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
+ itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This memo recurs"));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ if (response_enabled) {
+ g_signal_connect (
+ view, "response",
+ G_CALLBACK (view_response_cb), info);
+
+ itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST));
+
+ if (info->calendar_uid) {
+ start_calendar_server_by_uid (info, view, info->calendar_uid, info->type);
+ } else {
+ find_server (info, view, info->comp);
+ set_buttons_sensitive (info, view);
+ }
+ }
+}