diff options
author | JP Rosevear <jpr@novell.com> | 2004-12-30 02:39:11 +0800 |
---|---|---|
committer | JP Rosevear <jpr@src.gnome.org> | 2004-12-30 02:39:11 +0800 |
commit | a767f8cee2be954d01f2dcb630db6e3846c15c2a (patch) | |
tree | dc3c5e935c82323b3f6e5f53dfec17d951065ae6 /plugins/itip-formatter/itip-formatter.c | |
parent | a3a0a296b281dc1a4366dad845741a01f7712927 (diff) | |
download | gsoc2013-evolution-a767f8cee2be954d01f2dcb630db6e3846c15c2a.tar.gz gsoc2013-evolution-a767f8cee2be954d01f2dcb630db6e3846c15c2a.tar.zst gsoc2013-evolution-a767f8cee2be954d01f2dcb630db6e3846c15c2a.zip |
new protos
2004-12-29 JP Rosevear <jpr@novell.com>
* itip-view.h: new protos
* itip-view.c (format_date_and_time_x): don't draw the leading
zero in 12hr clock mode for the hour
(set_sender_text): make intro statements closer to the UI design
(set_description_text): display description
(set_info_items): show info items, messages with icons
(set_progress_text): show progress text item (for
loading/searching calendars)
(set_one_button): add a response button
(set_buttons): set response buttons based on mode
(itip_view_destroy): clear info items
(itip_view_class_init): add response signal
(itip_view_init): new areas for description, info items, buttons
(itip_view_set_description): accessor
(itip_view_get_description): ditto
(itip_view_add_info_item): add an info item to the display
(itip_view_clear_info_items): clear all items
(itip_view_set_progress): set the progress message
* itip-formatter.c: move over calendar loading, searching code,
set more itip view properties
* org-gnome-itip-formatter.eplug.in: add a config page item,
doesn't do much right now
svn path=/trunk/; revision=28207
Diffstat (limited to 'plugins/itip-formatter/itip-formatter.c')
-rw-r--r-- | plugins/itip-formatter/itip-formatter.c | 548 |
1 files changed, 540 insertions, 8 deletions
diff --git a/plugins/itip-formatter/itip-formatter.c b/plugins/itip-formatter/itip-formatter.c index c16cc24826..7c8a7ed9ab 100644 --- a/plugins/itip-formatter/itip-formatter.c +++ b/plugins/itip-formatter/itip-formatter.c @@ -35,17 +35,28 @@ #include <camel/camel-mime-message.h> #include <libecal/e-cal.h> #include <libecal/e-cal-time-util.h> +#include <libedataserverui/e-source-option-menu.h> +#include <libedataserverui/e-source-selector.h> #include <gtkhtml/gtkhtml-embedded.h> #include <mail/em-format-hook.h> +#include <mail/em-config.h> #include <mail/em-format-html.h> #include <e-util/e-account-list.h> #include <e-util/e-icon-factory.h> #include <calendar/gui/itip-utils.h> +#include <calendar/common/authentication.h> #include "itip-view.h" #define CLASSID "itip://" void format_itip (EPlugin *ep, EMFormatHookTarget *target); +GtkWidget *itip_formatter_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data); + +/* FIXME We should include these properly */ +icaltimezone *calendar_config_get_icaltimezone (void); +void calendar_config_init (void); +char *calendar_config_get_primary_calendar (void); +char *calendar_config_get_primary_tasks (void); typedef struct { EMFormatHTMLPObject pobject; @@ -58,12 +69,7 @@ typedef struct { ECal *current_ecal; ECalSourceType type; - char action; gboolean rsvp; - - GtkWidget *ok; - GtkWidget *hbox; - GtkWidget *vbox; char *vcalendar; ECalComponent *comp; @@ -87,6 +93,350 @@ typedef struct { gint view_only; } FormatItipPObject; +typedef struct { + FormatItipPObject *pitip; + char *uid; + int count; + gboolean show_selector; +} EItipControlFindData; + + +typedef void (* FormatItipOpenFunc) (ECal *ecal, ECalendarStatus status, gpointer data); + +static void +cal_opened_cb (ECal *ecal, ECalendarStatus status, gpointer data) +{ + FormatItipPObject *pitip = data; + ESource *source; + ECalSourceType source_type; + icaltimezone *zone; + + source_type = e_cal_get_source_type (ecal); + source = e_cal_get_source (ecal); + + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, cal_opened_cb, NULL); + + if (status != E_CALENDAR_STATUS_OK) { + itip_view_set_progress (ITIP_VIEW (pitip->view), "Failed to load at least one calendar"); + + g_hash_table_remove (pitip->ecals[source_type], e_source_peek_uid (source)); + + return; + } + + zone = calendar_config_get_icaltimezone (); + e_cal_set_default_timezone (ecal, zone, NULL); + + pitip->current_ecal = ecal; + +// set_ok_sens (itip); +} + +static ECal * +start_calendar_server (FormatItipPObject *pitip, ESource *source, ECalSourceType type, FormatItipOpenFunc func, gpointer data) +{ + ECal *ecal; + + ecal = g_hash_table_lookup (pitip->ecals[type], e_source_peek_uid (source)); + if (ecal) { + pitip->current_ecal = ecal; + + itip_view_set_progress (ITIP_VIEW (pitip->view), NULL); +// set_ok_sens (itip); + return ecal; + } + + ecal = auth_new_cal_from_source (source, type); + g_signal_connect (G_OBJECT (ecal), "cal_opened", G_CALLBACK (func), data); + + g_hash_table_insert (pitip->ecals[type], g_strdup (e_source_peek_uid (source)), ecal); + + e_cal_open_async (ecal, TRUE); + + return ecal; +} + +static ECal * +start_calendar_server_by_uid (FormatItipPObject *pitip, const char *uid, ECalSourceType type) +{ + int i; + + for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) { + ESource *source; + + source = e_source_list_peek_source_by_uid (pitip->source_lists[i], uid); + if (source) + return start_calendar_server (pitip, source, type, cal_opened_cb, pitip); + } + + return NULL; +} + +static void +source_selected_cb (ESourceOptionMenu *esom, ESource *source, gpointer data) +{ + FormatItipPObject *pitip = data; + + g_message ("Source selected"); + + /* FIXME turn off buttons while we check the calendar for being open? */ + + start_calendar_server (pitip, source, pitip->type, cal_opened_cb, pitip); +} + +static void +find_cal_opened_cb (ECal *ecal, ECalendarStatus status, gpointer data) +{ + EItipControlFindData *fd = data; + FormatItipPObject *pitip = fd->pitip; + ESource *source; + ECalSourceType source_type; + icalcomponent *icalcomp; + icaltimezone *zone; + + source_type = e_cal_get_source_type (ecal); + source = e_cal_get_source (ecal); + + fd->count--; + + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, find_cal_opened_cb, NULL); + + if (status != E_CALENDAR_STATUS_OK) { + g_hash_table_remove (pitip->ecals[source_type], e_source_peek_uid (source)); + + goto cleanup; + } + + if (e_cal_get_object (ecal, fd->uid, NULL, &icalcomp, NULL)) { + icalcomponent_free (icalcomp); + + pitip->current_ecal = ecal; + + itip_view_set_progress (ITIP_VIEW (pitip->view), NULL); +// set_ok_sens (fd->itip); + } + + zone = calendar_config_get_icaltimezone (); + e_cal_set_default_timezone (ecal, zone, NULL); + + cleanup: + if (fd->count == 0) { + /* FIXME the box check is to see if the buttons are displayed i think */ + if (fd->show_selector && !pitip->current_ecal /*&& pitip->vbox*/) { + GtkWidget *esom; + ESource *source = NULL; + char *uid; + + switch (pitip->type) { + case E_CAL_SOURCE_TYPE_EVENT: + uid = calendar_config_get_primary_calendar (); + break; + case E_CAL_SOURCE_TYPE_TODO: + uid = calendar_config_get_primary_tasks (); + break; + default: + uid = NULL; + g_assert_not_reached (); + } + + if (uid) { + source = e_source_list_peek_source_by_uid (pitip->source_lists[pitip->type], uid); + g_free (uid); + } + + /* Try to create a default if there isn't one */ + if (!source) + source = e_source_list_peek_source_any (pitip->source_lists[pitip->type]); + + g_message ("Picking any source"); + esom = e_source_option_menu_new (pitip->source_lists[pitip->type]); + /* FIXME used to force the data to be kept alive, still do this? */ + g_signal_connect (esom, "source_selected", G_CALLBACK (source_selected_cb), fd->pitip); + + //gtk_box_pack_start (GTK_BOX (pitip->vbox), esom, FALSE, TRUE, 0); + gtk_widget_show (esom); + + /* FIXME What if there is no source? */ + if (source) { + e_source_option_menu_select (E_SOURCE_OPTION_MENU (esom), source); + itip_view_set_progress (ITIP_VIEW (pitip->view), NULL); + } + } else { + /* FIXME Display error message to user */ + } + + g_free (fd->uid); + g_free (fd); + } +} + +static void +find_server (FormatItipPObject *pitip, ECalComponent *comp, gboolean show_selector) +{ + EItipControlFindData *fd = NULL; + GSList *groups, *l; + const char *uid; + + e_cal_component_get_uid (comp, &uid); + + itip_view_set_progress (ITIP_VIEW (pitip->view), "Searching for an existing version of this appointment"); + + groups = e_source_list_peek_groups (pitip->source_lists[pitip->type]); + for (l = groups; l; l = l->next) { + ESourceGroup *group; + GSList *sources, *m; + + group = l->data; + + sources = e_source_group_peek_sources (group); + for (m = sources; m; m = m->next) { + ESource *source; + ECal *ecal; + + source = m->data; + + if (!fd) { + fd = g_new0 (EItipControlFindData, 1); + fd->pitip = pitip; + fd->uid = g_strdup (uid); + fd->show_selector = show_selector; + } + fd->count++; + + ecal = start_calendar_server (pitip, source, pitip->type, find_cal_opened_cb, fd); + } + } +} + +static void +cleanup_ecal (ECal *ecal) +{ + /* Clean up any signals */ + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, cal_opened_cb, NULL); + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, find_cal_opened_cb, NULL); + + g_object_unref (ecal); +} + +static icalproperty * +find_attendee (icalcomponent *ical_comp, const char *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)) { + icalvalue *value; + const char *attendee; + char *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); + if (!g_strcasecmp (address, text)) { + g_free (text); + break; + } + g_free (text); + } + + return prop; +} + +static gboolean +change_status (icalcomponent *ical_comp, const char *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 { + EAccount *a; + + a = itip_addresses_get_default (); + + prop = icalproperty_new_attendee (a->id->address); + icalcomponent_add_property (ical_comp, prop); + + param = icalparameter_new_cn (a->id->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); + } + } + + return TRUE; +} + +static void +update_item (FormatItipPObject *pitip) +{ + struct icaltimetype stamp; + icalproperty *prop; + icalcomponent *clone; +// GtkWidget *dialog; + GError *error = NULL; + + /* 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 ()); + prop = icalproperty_new_x (icaltime_as_ical_string (stamp)); + 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 (!e_cal_receive_objects (pitip->current_ecal, pitip->top_level, &error)) { + /* FIXME e-error */ +// dialog = gnome_warning_dialog (error->message); + g_error_free (error); + } else { + /* FIXME I think we should do nothing */ +// dialog = gnome_ok_dialog (_("Update complete\n")); + } +// gnome_dialog_run_and_close (GNOME_DIALOG (dialog)); + + icalcomponent_remove_component (pitip->top_level, clone); +} + static icalcomponent * get_next (icalcompiter *iter) { @@ -191,6 +541,42 @@ extract_itip_data (FormatItipPObject *pitip) // show_current (itip); } +static void +view_response_cb (GtkWidget *widget, ItipViewResponse response, gpointer data) +{ + FormatItipPObject *pitip = data; + gboolean status = FALSE; + + switch (response) { + case ITIP_VIEW_RESPONSE_ACCEPT: + status = change_status (pitip->ical_comp, pitip->my_address, + ICAL_PARTSTAT_ACCEPTED); + if (status) { + e_cal_component_rescan (pitip->comp); + update_item (pitip); + } + break; + case ITIP_VIEW_RESPONSE_TENTATIVE: + status = change_status (pitip->ical_comp, pitip->my_address, + ICAL_PARTSTAT_TENTATIVE); + if (status) { + e_cal_component_rescan (pitip->comp); + update_item (pitip); + } + break; + case ITIP_VIEW_RESPONSE_DECLINE: + status = change_status (pitip->ical_comp, pitip->my_address, + ICAL_PARTSTAT_DECLINED); + if (status) { + e_cal_component_rescan (pitip->comp); + update_item (pitip); + } + break; + default: + break; + } +} + static gboolean format_itip_object (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject) { @@ -199,16 +585,58 @@ format_itip_object (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject ECalComponentOrganizer organizer; ECalComponentDateTime datetime; icaltimezone *from_zone, *to_zone; + GString *gstring = NULL; + GSList *list, *l; const char *string; - + int i; + + /* Source Lists and open ecal clients */ + for (i = 0; i < E_CAL_SOURCE_TYPE_LAST; i++) { + if (!e_cal_get_sources (&pitip->source_lists[E_CAL_SOURCE_TYPE_EVENT], E_CAL_SOURCE_TYPE_EVENT, NULL)) + /* FIXME More error handling? */ + pitip->source_lists[i] = NULL; + + /* Initialize the ecal hashes */ + pitip->ecals[i] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, cleanup_ecal); + } + /* FIXME Error handling? */ + /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */ extract_itip_data (pitip); pitip->view = itip_view_new (); gtk_widget_show (pitip->view); - itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_REQUEST); - + switch (pitip->method) { + case ICAL_METHOD_PUBLISH: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_PUBLISH); + break; + case ICAL_METHOD_REQUEST: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_REQUEST); + break; + case ICAL_METHOD_REPLY: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_REPLY); + break; + case ICAL_METHOD_ADD: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_ADD); + break; + case ICAL_METHOD_CANCEL: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_CANCEL); + break; + case ICAL_METHOD_REFRESH: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_REFRESH); + break; + case ICAL_METHOD_COUNTER: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_COUNTER); + break; + case ICAL_METHOD_DECLINECOUNTER: + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_DECLINECOUNTER); + break; + default: + /* FIXME What to do here? */ + itip_view_set_mode (ITIP_VIEW (pitip->view), ITIP_VIEW_MODE_ERROR); + } + e_cal_component_get_organizer (pitip->comp, &organizer); itip_view_set_organizer (ITIP_VIEW (pitip->view), organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value)); /* FIXME, do i need to strip the sentby somehow? Maybe with camel? */ @@ -220,6 +648,23 @@ format_itip_object (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject e_cal_component_get_location (pitip->comp, &string); itip_view_set_location (ITIP_VIEW (pitip->view), string); + e_cal_component_get_location (pitip->comp, &string); + itip_view_set_location (ITIP_VIEW (pitip->view), string); + + e_cal_component_get_description_list (pitip->comp, &list); + for (l = list; l; l = l->next) { + ECalComponentText *text = l->data; + + if (!gstring) + gstring = g_string_new (text->value); + else + g_string_append_printf (gstring, "\n\n%s", text->value); + } + e_cal_component_free_text_list (list); + + itip_view_set_description (ITIP_VIEW (pitip->view), gstring->str); + g_string_free (gstring, TRUE); + to_zone = calendar_config_get_icaltimezone (); e_cal_component_get_dtstart (pitip->comp, &datetime); @@ -254,8 +699,18 @@ format_itip_object (EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject } e_cal_component_free_datetime (&datetime); + /* Info area items */ + itip_view_add_info_item (ITIP_VIEW (pitip->view), ITIP_VIEW_INFO_ITEM_TYPE_INFO, "This meeting occurs weekly indefinitely"); + itip_view_add_info_item (ITIP_VIEW (pitip->view), ITIP_VIEW_INFO_ITEM_TYPE_WARNING, "An appointment in the calendar conflicts with this meeting"); + gtk_container_add (GTK_CONTAINER (eb), pitip->view); + gtk_widget_set_usize (pitip->view, 640, -1); + + g_signal_connect (pitip->view, "response", G_CALLBACK (view_response_cb), pitip); + /* FIXME Show selector should be handled in the itip view */ + find_server (pitip, pitip->comp, TRUE); + return TRUE; } @@ -273,3 +728,80 @@ format_itip (EPlugin *ep, EMFormatHookTarget *target) camel_stream_printf (target->stream, "<td valign=top><object classid=\"%s\"></object></td><td width=100%% valign=top>", CLASSID); camel_stream_printf (target->stream, "</td></tr></table>"); } + +GtkWidget * +itip_formatter_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data) +{ +// EMConfigTargetPrefs *target = (EMConfigTargetPrefs *) hook_data->config->target; + GtkWidget *page; + GtkWidget *tab_label; + GtkWidget *frame; + GtkWidget *frame_label; + GtkWidget *padding_label; + GtkWidget *hbox; + GtkWidget *inner_vbox; + GtkWidget *check; + GtkWidget *check_gaim; + + /* A structure to pass some stuff around */ +// stuff = g_new0 (struct bbdb_stuff, 1); +// stuff->target = target; + + /* Create a new notebook page */ + page = gtk_vbox_new (FALSE, 0); + GTK_CONTAINER (page)->border_width = 12; + tab_label = gtk_label_new (_("Meetings")); + gtk_notebook_append_page (GTK_NOTEBOOK (hook_data->parent), page, tab_label); + + /* Frame */ + frame = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0); + + /* "Automatic Contacts" */ + frame_label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL (frame_label), _("<span weight=\"bold\">General</span>")); + GTK_MISC (frame_label)->xalign = 0.0; + gtk_box_pack_start (GTK_BOX (frame), frame_label, FALSE, FALSE, 0); + + /* Indent/padding */ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (frame), hbox, FALSE, TRUE, 0); + padding_label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX (hbox), padding_label, FALSE, FALSE, 0); + inner_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), inner_vbox, FALSE, FALSE, 0); + + /* Enable BBDB checkbox */ + check = gtk_check_button_new_with_mnemonic (_("_Delete message after acting")); +// gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), gconf_client_get_bool (target->gconf, GCONF_KEY_ENABLE, NULL)); +// g_signal_connect (GTK_TOGGLE_BUTTON (check), "toggled", G_CALLBACK (enable_toggled_cb), stuff); + gtk_box_pack_start (GTK_BOX (inner_vbox), check, FALSE, FALSE, 0); +// stuff->check = check; + + /* "Instant Messaging Contacts" */ + frame = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (page), frame, TRUE, TRUE, 24); + + frame_label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL (frame_label), _("<span weight=\"bold\">Conflict Search</span>")); + GTK_MISC (frame_label)->xalign = 0.0; + gtk_box_pack_start (GTK_BOX (frame), frame_label, FALSE, FALSE, 0); + + /* Indent/padding */ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (frame), hbox, FALSE, TRUE, 0); + padding_label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX (hbox), padding_label, FALSE, FALSE, 0); + inner_vbox = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox), inner_vbox, FALSE, FALSE, 0); + + /* Enable Gaim Checkbox */ + check_gaim = gtk_label_new (_("Select the calendars to search for meeting conflicts")); + gtk_box_pack_start (GTK_BOX (inner_vbox), check_gaim, FALSE, FALSE, 0); +// stuff->check_gaim = check_gaim; + + gtk_widget_show_all (page); + + return page; +} + |