diff options
Diffstat (limited to 'calendar')
-rw-r--r-- | calendar/ChangeLog | 68 | ||||
-rw-r--r-- | calendar/cal-util/cal-component.c | 608 | ||||
-rw-r--r-- | calendar/cal-util/cal-component.h | 74 | ||||
-rw-r--r-- | calendar/gui/Makefile.am | 21 | ||||
-rw-r--r-- | calendar/gui/dialogs/Makefile.am | 7 | ||||
-rw-r--r-- | calendar/gui/dialogs/cancel-comp.c | 84 | ||||
-rw-r--r-- | calendar/gui/dialogs/cancel-comp.h | 30 | ||||
-rw-r--r-- | calendar/gui/dialogs/comp-editor-page.c | 51 | ||||
-rw-r--r-- | calendar/gui/dialogs/comp-editor-page.h | 11 | ||||
-rw-r--r-- | calendar/gui/dialogs/comp-editor.c | 461 | ||||
-rw-r--r-- | calendar/gui/dialogs/comp-editor.h | 42 | ||||
-rw-r--r-- | calendar/gui/dialogs/event-editor.c | 111 | ||||
-rw-r--r-- | calendar/gui/dialogs/meeting-page.c | 739 | ||||
-rw-r--r-- | calendar/gui/dialogs/meeting-page.glade | 103 | ||||
-rw-r--r-- | calendar/gui/dialogs/recurrence-page.c | 10 | ||||
-rw-r--r-- | calendar/gui/dialogs/send-comp.c | 84 | ||||
-rw-r--r-- | calendar/gui/dialogs/send-comp.h | 30 | ||||
-rw-r--r-- | calendar/gui/e-itip-control.c | 487 | ||||
-rw-r--r-- | calendar/gui/itip-utils.c | 228 | ||||
-rw-r--r-- | calendar/gui/itip-utils.h | 14 |
20 files changed, 2611 insertions, 652 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index a00da94dbc..42b1a81d6c 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,71 @@ +2001-06-19 JP Rosevear <jpr@ximian.com> + + * gui/dialogs/send-comp.c: itip/imip send dialog + + * gui/dialogs/send-comp.h: new proto + + * gui/dialogs/recurrence-page.c (recurrence_page_set_dates): only + use the weekday picker if visible + + * gui/dialogs/meeting-page.c: just show the meeting list + + * gui/dialogs/event-editor.c (event_editor_edit_comp): remove the + meeting page if no attendees + (schedule_meeting_cmd): schedule a meeting menu item + (refresh_meeting_cmd): refresh meeting request menu item + (cancel_meeting_cmd): ditto for cancel + (forward_cmd): send as attachment + + * gui/dialogs/comp-editor.c (comp_editor_remove_page): remove page + from dialog + (comp_editor_show_page): show a given page + (comp_editor_get_current_comp): return a cal component + representing the current widget state + (comp_editor_save_comp): save the cal component + (comp_editor_delete_comp): delete the cal component + (comp_editor_send_comp): send the cal component + (comp_editor_merge_ui): merge xml in to the bonobo gui + (setup_widgets): use a bonobo window instead of a gtk window, add menus again + (save_as_cmd): save to file on disk - still broken + (save_close_cmd): close menu command + (save_close_cmd): save and close menu command + + * gui/dialogs/comp-editor.h: new protos + + * gui/dialogs/cancel-comp.c (cancel_component_dialog): itip/imip + cancellation dialog + + * gui/dialogs/cancel-comp.h: new proto + + * gui/dialogs/Makefile.am: build new files + + * gui/dialogs/comp-editor-page.c + (comp_editor_page_notify_needs_send): emit needs_send signal + + * gui/dialogs/comp-editor-page.h: new signal protos + + * gui/itip-utils.c (itip_send_comp): new function to send cal + components + + * gui/itip-utils.h: new proto + + * gui/e-itip-control.c (pstream_load): trim using cal-component + wrapper stuff + (accept_button_clicked_cb): use itip_send_comp + (tentative_button_clicked_cb): ditto + (decline_button_clicked_cb): ditto + + * gui/Makefile.am: compile select name idl stuff + + * cal-util/cal-component.c (cal_component_get_organizer): get the organizer + (cal_component_set_organizer): set the organizer + (cal_component_get_recurid): get the recurrence id + (cal_component_set_recurid): set the recurrence id + (set_attendee_list): actually set the attendee list + (get_attendee_list): build the attendee list + + * cal-util/cal-component.h: new protos + 2001-06-19 Damon Chaplin <damon@ximian.com> * gui/dialogs/task-details-page.glade: diff --git a/calendar/cal-util/cal-component.c b/calendar/cal-util/cal-component.c index d716c0e581..28b60072c5 100644 --- a/calendar/cal-util/cal-component.c +++ b/calendar/cal-util/cal-component.c @@ -43,6 +43,22 @@ struct _CalComponentPrivate { icalproperty *status; + struct attendee { + icalproperty *prop; + icalparameter *cutype_param; + icalparameter *member_param; + icalparameter *role_param; + icalparameter *partstat_param; + icalparameter *rsvp_param; + icalparameter *delto_param; + icalparameter *delfrom_param; + icalparameter *sentby_param; + icalparameter *cn_param; + icalparameter *language_param; + }; + + GSList *attendee_list; + icalproperty *categories; icalproperty *classification; @@ -74,6 +90,15 @@ struct _CalComponentPrivate { GSList *exdate_list; /* list of struct datetime */ GSList *exrule_list; /* list of icalproperty objects */ + struct organizer { + icalproperty *prop; + icalparameter *sentby_param; + icalparameter *cn_param; + icalparameter *language_param; + }; + + struct organizer organizer; + icalproperty *geo; icalproperty *last_modified; icalproperty *percent; @@ -84,6 +109,14 @@ struct _CalComponentPrivate { icalparameter *value_param; }; + struct recur_id { + struct datetime recur_time; + + icalparameter *range_param; + }; + + struct recur_id recur_id; + GSList *rdate_list; /* list of struct period */ GSList *rrule_list; /* list of icalproperty objects */ @@ -404,6 +437,29 @@ cal_component_clone (CalComponent *comp) return new_comp; } +/* Scans an attendee property */ +static void +scan_attendee (CalComponent *comp, GSList **attendee_list, icalproperty *prop) +{ + struct attendee *attendee; + + attendee = g_new (struct attendee, 1); + attendee->prop = prop; + + attendee->cutype_param = icalproperty_get_first_parameter (prop, ICAL_CUTYPE_PARAMETER); + attendee->member_param = icalproperty_get_first_parameter (prop, ICAL_MEMBER_PARAMETER); + attendee->role_param = icalproperty_get_first_parameter (prop, ICAL_ROLE_PARAMETER); + attendee->partstat_param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER); + attendee->rsvp_param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER); + attendee->delto_param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDTO_PARAMETER); + attendee->delfrom_param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER); + attendee->sentby_param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); + attendee->cn_param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER); + attendee->language_param = icalproperty_get_first_parameter (prop, ICAL_LANGUAGE_PARAMETER); + + *attendee_list = g_slist_append (*attendee_list, attendee); +} + /* Scans a date/time and timezone pair property */ static void scan_datetime (CalComponent *comp, struct datetime *datetime, icalproperty *prop) @@ -432,6 +488,17 @@ scan_exdate (CalComponent *comp, icalproperty *prop) priv->exdate_list = g_slist_append (priv->exdate_list, dt); } +/* Scans and attendee property */ +static void +scan_organizer (CalComponent *comp, struct organizer *organizer, icalproperty *prop) +{ + organizer->prop = prop; + + organizer->sentby_param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); + organizer->cn_param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER); + organizer->language_param = icalproperty_get_first_parameter (prop, ICAL_LANGUAGE_PARAMETER); +} + /* Scans an icalperiodtype property */ static void scan_period (CalComponent *comp, GSList **list, icalproperty *prop) @@ -445,6 +512,16 @@ scan_period (CalComponent *comp, GSList **list, icalproperty *prop) *list = g_slist_append (*list, period); } + +/* Scans an icalrecurtype property */ +static void +scan_recur_id (CalComponent *comp, struct recur_id *recur_id, icalproperty *prop) +{ + scan_datetime (comp, &recur_id->recur_time, prop); + + recur_id->range_param = icalproperty_get_first_parameter (prop, ICAL_RANGE_PARAMETER); +} + /* Scans an icalrecurtype property */ static void scan_recur (CalComponent *comp, GSList **list, icalproperty *prop) @@ -493,6 +570,10 @@ scan_property (CalComponent *comp, icalproperty *prop) priv->status = prop; break; + case ICAL_ATTENDEE_PROPERTY: + scan_attendee (comp, &priv->attendee_list, prop); + break; + case ICAL_CATEGORIES_PROPERTY: priv->categories = prop; break; @@ -549,6 +630,10 @@ scan_property (CalComponent *comp, icalproperty *prop) priv->last_modified = prop; break; + case ICAL_ORGANIZER_PROPERTY: + scan_organizer (comp, &priv->organizer, prop); + break; + case ICAL_PERCENTCOMPLETE_PROPERTY: priv->percent = prop; break; @@ -557,6 +642,10 @@ scan_property (CalComponent *comp, icalproperty *prop) priv->priority = prop; break; + case ICAL_RECURRENCEID_PROPERTY: + scan_recur_id (comp, &priv->recur_id, prop); + break; + case ICAL_RDATE_PROPERTY: scan_period (comp, &priv->rdate_list, prop); break; @@ -1830,7 +1919,7 @@ cal_component_set_dtend (CalComponent *comp, CalComponentDateTime *dt) /** * cal_component_get_dtstamp: * @comp: A calendar component object. - * @t: Return value for the date/timestamp. + * @t: A value for the date/timestamp. * * Queries the date/timestamp property of a calendar component object, which is * the last time at which the object was modified by a calendar user agent. @@ -2531,6 +2620,145 @@ cal_component_set_last_modified (CalComponent *comp, struct icaltimetype *t) } /** + * cal_component_get_organizer: + * @comp: A calendar component object + * @organizer: A value for the organizer + * + * Queries the organizer property of a calendar component object + **/ +void +cal_component_get_organizer (CalComponent *comp, CalComponentOrganizer *organizer) +{ + CalComponentPrivate *priv; + + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + g_return_if_fail (organizer != NULL); + + priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); + + if (priv->organizer.prop) + organizer->value = icalproperty_get_organizer (priv->organizer.prop); + else + organizer->value = NULL; + + if (priv->organizer.sentby_param) + organizer->sentby = icalparameter_get_sentby (priv->organizer.sentby_param); + else + organizer->sentby = NULL; + + if (priv->organizer.cn_param) + organizer->cn = icalparameter_get_sentby (priv->organizer.cn_param); + else + organizer->cn = NULL; + + if (priv->organizer.language_param) + organizer->language = icalparameter_get_sentby (priv->organizer.language_param); + else + organizer->language = NULL; + +} + +/** + * cal_component_set_organizer: + * @comp: A calendar component object. + * @organizer: Value for the organizer property + * + * Sets the organizer of a calendar component object + **/ +void +cal_component_set_organizer (CalComponent *comp, CalComponentOrganizer *organizer) +{ + CalComponentPrivate *priv; + + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + + priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); + + if (!organizer) { + if (priv->organizer.prop) { + icalcomponent_remove_property (priv->icalcomp, priv->organizer.prop); + icalproperty_free (priv->organizer.prop); + + priv->organizer.prop = NULL; + priv->organizer.sentby_param = NULL; + priv->organizer.cn_param = NULL; + priv->organizer.language_param = NULL; + } + + return; + } + + g_return_if_fail (organizer->value != NULL); + + if (priv->organizer.prop) + icalproperty_set_organizer (priv->organizer.prop, (char *) organizer->value); + else { + priv->organizer.prop = icalproperty_new_organizer ((char *) organizer->value); + icalcomponent_add_property (priv->icalcomp, priv->organizer.prop); + } + + if (organizer->sentby) { + g_assert (priv->organizer.prop != NULL); + + if (priv->organizer.sentby_param) + icalparameter_set_sentby (priv->organizer.sentby_param, + (char *) organizer->sentby); + else { + priv->organizer.sentby_param = icalparameter_new_sentby ( + (char *) organizer->sentby); + icalproperty_add_parameter (priv->organizer.prop, + priv->organizer.sentby_param); + } + } else if (priv->organizer.sentby_param) { + icalproperty_remove_parameter (priv->organizer.prop, ICAL_SENTBY_PARAMETER); + icalparameter_free (priv->organizer.sentby_param); + priv->organizer.sentby_param = NULL; + } + + if (organizer->cn) { + g_assert (priv->organizer.prop != NULL); + + if (priv->organizer.cn_param) + icalparameter_set_cn (priv->organizer.cn_param, + (char *) organizer->cn); + else { + priv->organizer.cn_param = icalparameter_new_cn ( + (char *) organizer->cn); + icalproperty_add_parameter (priv->organizer.prop, + priv->organizer.cn_param); + } + } else if (priv->organizer.cn_param) { + icalproperty_remove_parameter (priv->organizer.prop, ICAL_CN_PARAMETER); + icalparameter_free (priv->organizer.cn_param); + priv->organizer.cn_param = NULL; + } + + if (organizer->language) { + g_assert (priv->organizer.prop != NULL); + + if (priv->organizer.language_param) + icalparameter_set_language (priv->organizer.language_param, + (char *) organizer->language); + else { + priv->organizer.language_param = icalparameter_new_language ( + (char *) organizer->language); + icalproperty_add_parameter (priv->organizer.prop, + priv->organizer.language_param); + } + } else if (priv->organizer.language_param) { + icalproperty_remove_parameter (priv->organizer.prop, ICAL_LANGUAGE_PARAMETER); + icalparameter_free (priv->organizer.language_param); + priv->organizer.language_param = NULL; + } + + +} + +/** * cal_component_get_percent: * @comp: A calendar component object. * @percent: Return value for the percent-complete property. This should be @@ -2661,6 +2889,54 @@ cal_component_set_priority (CalComponent *comp, int *priority) } /** + * cal_component_get_recurid: + * @comp: A calendar component object. + * @recur_id: Return value for the recurrence id property + * + * Queries the recurrence id property of a calendar component object + **/ +void +cal_component_get_recurid (CalComponent *comp, CalComponentRange **recur_id) +{ + CalComponentPrivate *priv; + + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + g_return_if_fail (recur_id != NULL); + + priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); + + get_datetime (&priv->recur_id.recur_time, + icalproperty_get_recurrenceid, + (*recur_id)->datetime); +} + +/** + * cal_component_set_recurid: + * @comp: A calendar component object. + * @recur_id: Value for the recurrence id property. + * + * Sets the recurrence id property of a calendar component object. + **/ +void +cal_component_set_recurid (CalComponent *comp, CalComponentRange *recur_id) +{ + CalComponentPrivate *priv; + + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + + priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); + + set_datetime (comp, &priv->recur_id.recur_time, + icalproperty_new_recurrenceid, + icalproperty_set_recurrenceid, + recur_id->datetime); +} + +/** * cal_component_get_rdate_list: * @comp: A calendar component object. * @period_list: Return value for the list of recurrence dates, as a list of @@ -3212,6 +3488,312 @@ cal_component_set_url (CalComponent *comp, const char *url) } } +/* Gets a text list value */ +static void +get_attendee_list (GSList *attendee_list, GSList **al) +{ + GSList *l; + + *al = NULL; + + if (!attendee_list) + return; + + for (l = attendee_list; l; l = l->next) { + struct attendee *attendee; + CalComponentAttendee *a; + + attendee = l->data; + g_assert (attendee->prop != NULL); + + a = g_new0 (CalComponentAttendee, 1); + a->value = icalproperty_get_attendee (attendee->prop); + + if (attendee->member_param) + a->member = icalparameter_get_member (attendee->member_param); + if (attendee->cutype_param) { + switch (icalparameter_get_cutype (attendee->cutype_param)) { + case ICAL_CUTYPE_INDIVIDUAL: + a->cutype = CAL_COMPONENT_CUTYPE_INDIVIDUAL; + break; + case ICAL_CUTYPE_GROUP: + a->cutype = CAL_COMPONENT_CUTYPE_GROUP; + break; + case ICAL_CUTYPE_RESOURCE: + a->cutype = CAL_COMPONENT_CUTYPE_RESOURCE; + break; + case ICAL_CUTYPE_ROOM: + a->cutype = CAL_COMPONENT_CUTYPE_ROOM; + break; + default: + a->cutype = CAL_COMPONENT_CUTYPE_UNKNOWN; + } + } + + if (attendee->role_param) { + switch (icalparameter_get_role (attendee->role_param)) { + case ICAL_ROLE_CHAIR: + a->role = CAL_COMPONENT_ROLE_CHAIR; + break; + case ICAL_ROLE_REQPARTICIPANT: + a->role = CAL_COMPONENT_ROLE_REQUIRED; + break; + case ICAL_ROLE_OPTPARTICIPANT: + a->role = CAL_COMPONENT_ROLE_OPTIONAL; + break; + case ICAL_ROLE_NONPARTICIPANT: + a->role = CAL_COMPONENT_ROLE_NON; + break; + default: + a->role = CAL_COMPONENT_ROLE_UNKNOWN; + } + } + + if (attendee->partstat_param) { + switch (icalparameter_get_role (attendee->partstat_param)) { + case ICAL_PARTSTAT_NEEDSACTION: + a->status = CAL_COMPONENT_PARTSTAT_NEEDSACTION; + break; + case ICAL_PARTSTAT_ACCEPTED: + a->status = CAL_COMPONENT_PARTSTAT_ACCEPTED; + break; + case ICAL_PARTSTAT_DECLINED: + a->status = CAL_COMPONENT_PARTSTAT_DECLINED; + break; + case ICAL_PARTSTAT_TENTATIVE: + a->status = CAL_COMPONENT_PARTSTAT_TENTATIVE; + break; + case ICAL_PARTSTAT_DELEGATED: + a->status = CAL_COMPONENT_PARTSTAT_DELEGATED; + break; + case ICAL_PARTSTAT_COMPLETED: + a->status = CAL_COMPONENT_PARTSTAT_COMPLETED; + break; + case ICAL_PARTSTAT_INPROCESS: + a->status = CAL_COMPONENT_PARTSTAT_INPROCESS; + break; + default: + a->status = CAL_COMPONENT_PARTSTAT_UNKNOWN; + } + } + + if (attendee->rsvp_param && icalparameter_get_rsvp (attendee->rsvp_param) == ICAL_RSVP_TRUE) + a->rsvp = TRUE; + else + a->rsvp = FALSE; + + if (attendee->delfrom_param) + a->delfrom = icalparameter_get_sentby (attendee->delfrom_param); + if (attendee->delto_param) + a->delto = icalparameter_get_sentby (attendee->delto_param); + if (attendee->sentby_param) + a->sentby = icalparameter_get_sentby (attendee->sentby_param); + if (attendee->cn_param) + a->cn = icalparameter_get_sentby (attendee->cn_param); + if (attendee->language_param) + a->language = icalparameter_get_sentby (attendee->language_param); + + *al = g_slist_prepend (*al, a); + } + + *al = g_slist_reverse (*al); +} + + +/* Sets a text list value */ +static void +set_attendee_list (CalComponent *comp, + GSList **attendee_list, + GSList *al) +{ + CalComponentPrivate *priv; + GSList *l; + + priv = comp->priv; + + /* Remove old attendees */ + + for (l = *attendee_list; l; l = l->next) { + struct attendee *attendee; + + attendee = l->data; + g_assert (attendee->prop != NULL); + + icalcomponent_remove_property (priv->icalcomp, attendee->prop); + icalproperty_free (attendee->prop); + g_free (attendee); + } + + g_slist_free (*attendee_list); + *attendee_list = NULL; + + /* Add in new attendees */ + + for (l = al; l; l = l->next) { + CalComponentAttendee *a; + struct attendee *attendee; + icalparameter_cutype ct; + icalparameter_role r; + icalparameter_partstat p; + + a = l->data; + g_return_if_fail (a->value != NULL); + + attendee = g_new0 (struct attendee, 1); + + attendee->prop = icalproperty_new_attendee (a->value); + icalcomponent_add_property (priv->icalcomp, attendee->prop); + + if (a->member) { + attendee->member_param = icalparameter_new_member (a->member); + icalproperty_add_parameter (attendee->prop, attendee->member_param); + } + + switch (a->cutype) { + case CAL_COMPONENT_CUTYPE_INDIVIDUAL: + ct = ICAL_CUTYPE_INDIVIDUAL; + break; + case CAL_COMPONENT_CUTYPE_GROUP: + ct = ICAL_CUTYPE_GROUP; + break; + case CAL_COMPONENT_CUTYPE_RESOURCE: + ct = ICAL_CUTYPE_RESOURCE; + break; + case CAL_COMPONENT_CUTYPE_ROOM: + ct = ICAL_CUTYPE_ROOM; + break; + default: + ct = ICAL_CUTYPE_UNKNOWN; + } + attendee->cutype_param = icalparameter_new_cutype (ct); + icalproperty_add_parameter (attendee->prop, attendee->cutype_param); + + switch (a->role) { + case CAL_COMPONENT_ROLE_CHAIR: + r = ICAL_ROLE_CHAIR; + break; + case CAL_COMPONENT_ROLE_REQUIRED: + r = ICAL_ROLE_REQPARTICIPANT; + break; + case CAL_COMPONENT_ROLE_OPTIONAL: + r = ICAL_ROLE_OPTPARTICIPANT; + break; + case CAL_COMPONENT_ROLE_NON: + r = ICAL_ROLE_NONPARTICIPANT; + break; + default: + r = ICAL_ROLE_NONE; + } + attendee->role_param = icalparameter_new_role (r); + icalproperty_add_parameter (attendee->prop, attendee->role_param); + + switch (a->status) { + case CAL_COMPONENT_PARTSTAT_NEEDSACTION: + p = ICAL_PARTSTAT_NEEDSACTION; + break; + case CAL_COMPONENT_PARTSTAT_ACCEPTED: + p = ICAL_PARTSTAT_ACCEPTED; + break; + case CAL_COMPONENT_PARTSTAT_DECLINED: + p = ICAL_PARTSTAT_DECLINED; + break; + case CAL_COMPONENT_PARTSTAT_TENTATIVE: + p = ICAL_PARTSTAT_TENTATIVE; + break; + case CAL_COMPONENT_PARTSTAT_DELEGATED: + p = ICAL_PARTSTAT_DELEGATED; + break; + case CAL_COMPONENT_PARTSTAT_COMPLETED: + p = ICAL_PARTSTAT_COMPLETED; + break; + case CAL_COMPONENT_PARTSTAT_INPROCESS: + p = ICAL_PARTSTAT_INPROCESS; + break; + default: + p = ICAL_PARTSTAT_NONE; + } + attendee->partstat_param = icalparameter_new_partstat (p); + icalproperty_add_parameter (attendee->prop, attendee->partstat_param); + + if (a->rsvp) + attendee->rsvp_param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); + else + attendee->rsvp_param = icalparameter_new_rsvp (ICAL_RSVP_FALSE); + icalproperty_add_parameter (attendee->prop, attendee->rsvp_param); + + if (a->delfrom) { + attendee->delfrom_param = icalparameter_new_delegatedfrom (a->delfrom); + icalproperty_add_parameter (attendee->prop, attendee->delfrom_param); + } + if (a->delto) { + attendee->delto_param = icalparameter_new_delegatedto (a->delto); + icalproperty_add_parameter (attendee->prop, attendee->delfrom_param); + } + if (a->sentby) { + attendee->sentby_param = icalparameter_new_sentby (a->sentby); + icalproperty_add_parameter (attendee->prop, attendee->sentby_param); + } + if (a->cn) { + attendee->cn_param = icalparameter_new_cn (a->cn); + icalproperty_add_parameter (attendee->prop, attendee->cn_param); + } + if (a->language) { + attendee->language_param = icalparameter_new_language (a->cn); + icalproperty_add_parameter (attendee->prop, attendee->language_param); + } + + *attendee_list = g_slist_prepend (*attendee_list, attendee); + } + + *attendee_list = g_slist_reverse (*attendee_list); +} + +/** + * cal_component_get_attendee_list: + * @comp: A calendar component object. + * @attendee_list: Return value for the attendee property. + * This should be freed using the cal_component_free_attendee_list () + * function. + * + * Queries the attendee properties of the calendar component object + **/ +void +cal_component_get_attendee_list (CalComponent *comp, GSList **attendee_list) +{ + CalComponentPrivate *priv; + + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + g_return_if_fail (attendee_list != NULL); + + priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); + + get_attendee_list (priv->attendee_list, attendee_list); +} + +/** + * cal_component_set_attendee_list: + * @comp: A calendar component object. + * @attendee_list: Values for attendee properties + * + * Sets the attendees of a calendar component object + **/ +void +cal_component_set_attendee_list (CalComponent *comp, GSList *attendee_list) +{ + CalComponentPrivate *priv; + + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + + priv = comp->priv; + g_return_if_fail (priv->icalcomp != NULL); + + set_attendee_list (comp, &priv->attendee_list, attendee_list); +} + + /** @@ -3419,6 +4001,30 @@ cal_component_free_text_list (GSList *text_list) g_slist_free (text_list); } +/** + * cal_component_free_attendee_list: + * @attendee_list: + * + * + **/ +void +cal_component_free_attendee_list (GSList *attendee_list) +{ + GSList *l; + + for (l = attendee_list; l; l = l->next) { + CalComponentAttendee *attendee; + + g_assert (l->data != NULL); + + attendee = l->data; + g_return_if_fail (attendee != NULL); + g_free (attendee); + } + + g_slist_free (attendee_list); +} + /** diff --git a/calendar/cal-util/cal-component.h b/calendar/cal-util/cal-component.h index e4a2e2421e..4b67b24346 100644 --- a/calendar/cal-util/cal-component.h +++ b/calendar/cal-util/cal-component.h @@ -119,6 +119,19 @@ typedef struct { } u; } CalComponentPeriod; +/* The type of range */ +typedef enum { + CAL_COMPONENT_RANGE_SINGLE, + CAL_COMPONENT_RANGE_THISPRIOR, + CAL_COMPONENT_RANGE_THISFUTURE, +} CalComponentRangeType; + +typedef struct { + CalComponentRangeType type; + + CalComponentDateTime *datetime; +} CalComponentRange; + /* Text properties */ typedef struct { /* Description string */ @@ -136,6 +149,57 @@ typedef enum { CAL_COMPONENT_TRANSP_UNKNOWN } CalComponentTransparency; +/* Organizer & Attendee */ +typedef enum { + CAL_COMPONENT_CUTYPE_INDIVIDUAL, + CAL_COMPONENT_CUTYPE_GROUP, + CAL_COMPONENT_CUTYPE_RESOURCE, + CAL_COMPONENT_CUTYPE_ROOM, + CAL_COMPONENT_CUTYPE_UNKNOWN +} CalComponentCUType; + +typedef enum { + CAL_COMPONENT_ROLE_CHAIR, + CAL_COMPONENT_ROLE_REQUIRED, + CAL_COMPONENT_ROLE_OPTIONAL, + CAL_COMPONENT_ROLE_NON, + CAL_COMPONENT_ROLE_UNKNOWN +} CalComponentRole; + +typedef enum { + CAL_COMPONENT_PARTSTAT_NEEDSACTION, + CAL_COMPONENT_PARTSTAT_ACCEPTED, + CAL_COMPONENT_PARTSTAT_DECLINED, + CAL_COMPONENT_PARTSTAT_TENTATIVE, + CAL_COMPONENT_PARTSTAT_DELEGATED, + CAL_COMPONENT_PARTSTAT_COMPLETED, + CAL_COMPONENT_PARTSTAT_INPROCESS, + CAL_COMPONENT_PARTSTAT_UNKNOWN +} CalComponentPartStat; + +typedef struct { + const char *value; + + const char *member; + CalComponentCUType cutype; + CalComponentRole role; + CalComponentPartStat status; + gboolean rsvp; + + const char *delto; + const char *delfrom; + const char *sentby; + const char *cn; + const char *language; +} CalComponentAttendee; + +typedef struct { + const char *value; + const char *sentby; + const char *cn; + const char *language; +} CalComponentOrganizer; + /* Main calendar component object */ typedef struct _CalComponent CalComponent; @@ -227,12 +291,18 @@ void cal_component_set_geo (CalComponent *comp, struct icalgeotype *geo); void cal_component_get_last_modified (CalComponent *comp, struct icaltimetype **t); void cal_component_set_last_modified (CalComponent *comp, struct icaltimetype *t); +void cal_component_get_organizer (CalComponent *comp, CalComponentOrganizer *organizer); +void cal_component_set_organizer (CalComponent *comp, CalComponentOrganizer *organizer); + void cal_component_get_percent (CalComponent *comp, int **percent); void cal_component_set_percent (CalComponent *comp, int *percent); void cal_component_get_priority (CalComponent *comp, int **priority); void cal_component_set_priority (CalComponent *comp, int *priority); +void cal_component_get_recurid (CalComponent *comp, CalComponentRange **recur_id); +void cal_component_set_recurid (CalComponent *comp, CalComponentRange *recur_id); + void cal_component_get_rdate_list (CalComponent *comp, GSList **period_list); void cal_component_set_rdate_list (CalComponent *comp, GSList *period_list); gboolean cal_component_has_rdates (CalComponent *comp); @@ -259,6 +329,9 @@ void cal_component_set_transparency (CalComponent *comp, CalComponentTransparenc void cal_component_get_url (CalComponent *comp, const char **url); void cal_component_set_url (CalComponent *comp, const char *url); +void cal_component_get_attendee_list (CalComponent *comp, GSList **attendee_list); +void cal_component_set_attendee_list (CalComponent *comp, GSList *attendee_list); + gboolean cal_component_event_dates_match (CalComponent *comp1, CalComponent *comp2); /* Functions to free returned values */ @@ -274,6 +347,7 @@ void cal_component_free_period_list (GSList *period_list); void cal_component_free_recur_list (GSList *recur_list); void cal_component_free_sequence (int *sequence); void cal_component_free_text_list (GSList *text_list); +void cal_component_free_attendee_list (GSList *attendee_list); /* Alarms */ diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index 4aec1db792..6fd84da671 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -1,17 +1,24 @@ ## CORBA stuff IDLS = \ - $(srcdir)/../../composer/Evolution-Composer.idl - -IDL_GENERATED = \ - Evolution-Composer.h \ - Evolution-Composer-common.c \ - Evolution-Composer-skels.c \ - Evolution-Composer-stubs.c + $(top_srcdir)/composer/Evolution-Composer.idl \ + $(top_srcdir)/addressbook/gui/component/select-names/Evolution-Addressbook-SelectNames.idl + +IDL_GENERATED = \ +e Evolution-Composer.h \ + Evolution-Composer-common.c \ + Evolution-Composer-skels.c \ + Evolution-Composer-stubs.c \ + Evolution-Addressbook-SelectNames.h \ + Evolution-Addressbook-SelectNames-common.c \ + Evolution-Addressbook-SelectNames-skels.c \ + Evolution-Addressbook-SelectNames-stubs.c $(IDL_GENERATED): $(IDLS) $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl `$(GNOME_CONFIG) --cflags idl` \ $(srcdir)/../../composer/Evolution-Composer.idl + $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl `$(GNOME_CONFIG) --cflags idl` \ + $(top_srcdir)/addressbook/gui/component/select-names/Evolution-Addressbook-SelectNames.idl SUBDIRS = alarm-notify dialogs diff --git a/calendar/gui/dialogs/Makefile.am b/calendar/gui/dialogs/Makefile.am index de97ea2d45..1bd4b5034e 100644 --- a/calendar/gui/dialogs/Makefile.am +++ b/calendar/gui/dialogs/Makefile.am @@ -24,6 +24,8 @@ libcal_dialogs_a_SOURCES = \ alarm-page.h \ cal-prefs-dialog.c \ cal-prefs-dialog.h \ + cancel-comp.c \ + cancel-comp.h \ comp-editor.c \ comp-editor.h \ comp-editor-page.c \ @@ -38,10 +40,14 @@ libcal_dialogs_a_SOURCES = \ event-editor.h \ event-page.c \ event-page.h \ + meeting-page.c \ + meeting-page.h \ recurrence-page.c \ recurrence-page.h \ save-comp.c \ save-comp.h \ + send-comp.c \ + send-comp.h \ task-editor.c \ task-editor.h \ task-details-page.c \ @@ -55,6 +61,7 @@ glade_DATA = \ cal-prefs-dialog.glade \ e-timezone-dialog.glade \ event-page.glade \ + meeting-page.glade \ recurrence-page.glade \ task-details-page.glade \ task-page.glade diff --git a/calendar/gui/dialogs/cancel-comp.c b/calendar/gui/dialogs/cancel-comp.c new file mode 100644 index 0000000000..4362f9da7f --- /dev/null +++ b/calendar/gui/dialogs/cancel-comp.c @@ -0,0 +1,84 @@ +/* Evolution calendar - Send calendar component dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Author: JP Rosevear <jpr@ximian.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-i18n.h> +#include <libgnomeui/gnome-dialog.h> +#include <libgnomeui/gnome-dialog-util.h> +#include <libgnomeui/gnome-uidefs.h> +#include <gal/widgets/e-unicode.h> +#include "cancel-comp.h" + + + +/** + * cancel_component_dialog: + * + * Pops up a dialog box asking the user whether he wants to send a + * cancel and delete an iTip/iMip message + * + * Return value: TRUE if the user clicked Yes, FALSE otherwise. + **/ +gboolean +cancel_component_dialog (CalComponent *comp) +{ + GtkWidget *dialog; + CalComponentVType vtype; + char *str; + + str = _("The meeting status has changed. Send an updated version?"); + + vtype = cal_component_get_vtype (comp); + + switch (vtype) { + case CAL_COMPONENT_EVENT: + str = g_strdup_printf (_("Are you sure you want to cancel " + "and delete this meeting?")); + break; + + case CAL_COMPONENT_TODO: + str = g_strdup_printf (_("Are you sure you want to cancel " + "and delete this task?")); + break; + + case CAL_COMPONENT_JOURNAL: + str = g_strdup_printf (_("Are you sure you want to cancel " + "and delete this journal entry?")); + break; + + default: + g_message ("send_component_dialog(): " + "Cannot handle object of type %d", vtype); + return FALSE; + } + + dialog = gnome_question_dialog_modal (str, NULL, NULL); + + if (gnome_dialog_run (GNOME_DIALOG (dialog)) == GNOME_YES) + return TRUE; + else + return FALSE; +} diff --git a/calendar/gui/dialogs/cancel-comp.h b/calendar/gui/dialogs/cancel-comp.h new file mode 100644 index 0000000000..04f1768e3d --- /dev/null +++ b/calendar/gui/dialogs/cancel-comp.h @@ -0,0 +1,30 @@ +/* Evolution calendar - Send calendar component dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Author: JP Rosevear <jpr@ximian.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CANCEL_COMP_H +#define CANCEL_COMP_H + +#include <glib.h> +#include <cal-util/cal-component.h> + +gboolean cancel_component_dialog (CalComponent *comp); + +#endif diff --git a/calendar/gui/dialogs/comp-editor-page.c b/calendar/gui/dialogs/comp-editor-page.c index eab0a50566..005314d0c0 100644 --- a/calendar/gui/dialogs/comp-editor-page.c +++ b/calendar/gui/dialogs/comp-editor-page.c @@ -34,6 +34,7 @@ static void comp_editor_page_class_init (CompEditorPageClass *class); enum { CHANGED, + NEEDS_SEND, SUMMARY_CHANGED, DATES_CHANGED, LAST_SIGNAL @@ -95,6 +96,15 @@ comp_editor_page_class_init (CompEditorPageClass *class) gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); + comp_editor_page_signals[NEEDS_SEND] = + gtk_signal_new ("needs_send", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (CompEditorPageClass, + needs_send), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + comp_editor_page_signals[SUMMARY_CHANGED] = gtk_signal_new ("summary_changed", GTK_RUN_FIRST, @@ -189,6 +199,23 @@ comp_editor_page_fill_component (CompEditorPage *page, CalComponent *comp) } /** + * comp_editor_page_set_cal_client: + * @page: An editor page + * @client: A #CalClient object + * + * Sets the #CalClient for the dialog page to use. + **/ +void +comp_editor_page_set_cal_client (CompEditorPage *page, CalClient *client) +{ + g_return_if_fail (page != NULL); + g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); + + if (CLASS (page)->set_cal_client != NULL) + (* CLASS (page)->set_cal_client) (page, client); +} + +/** * comp_editor_page_set_summary: * @page: An editor page * @summary: The text of the new summary value @@ -223,36 +250,34 @@ comp_editor_page_set_dates (CompEditorPage *page, CompEditorPageDates *dates) } /** - * comp_editor_page_set_cal_client: - * @page: An editor page - * @client: A #CalClient object + * comp_editor_page_notify_changed: + * @page: An editor page. * - * Sets the #CalClient for the dialog page to use. + * Makes an editor page emit the "changed" signal. This is meant to be + * used only by page implementations. **/ void -comp_editor_page_set_cal_client (CompEditorPage *page, CalClient *client) +comp_editor_page_notify_changed (CompEditorPage *page) { g_return_if_fail (page != NULL); g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); - if (CLASS (page)->set_cal_client != NULL) - (* CLASS (page)->set_cal_client) (page, client); + gtk_signal_emit (GTK_OBJECT (page), comp_editor_page_signals[CHANGED]); } /** - * comp_editor_page_notify_changed: - * @page: An editor page. + * comp_editor_page_notify_needs_send: + * @page: + * * - * Makes an editor page emit the "changed" signal. This is meant to be - * used only by page implementations. **/ void -comp_editor_page_notify_changed (CompEditorPage *page) +comp_editor_page_notify_needs_send (CompEditorPage *page) { g_return_if_fail (page != NULL); g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); - gtk_signal_emit (GTK_OBJECT (page), comp_editor_page_signals[CHANGED]); + gtk_signal_emit (GTK_OBJECT (page), comp_editor_page_signals[NEEDS_SEND]); } /** diff --git a/calendar/gui/dialogs/comp-editor-page.h b/calendar/gui/dialogs/comp-editor-page.h index 931aa32d62..c4c31852cf 100644 --- a/calendar/gui/dialogs/comp-editor-page.h +++ b/calendar/gui/dialogs/comp-editor-page.h @@ -54,9 +54,11 @@ typedef struct { /* Notification signals */ - void (* changed) (CompEditorPage *page); + void (* changed) (CompEditorPage *page); + void (* needs_send) (CompEditorPage *page); + void (* summary_changed) (CompEditorPage *page, const char *summary); - void (* dates_changed) (CompEditorPage *page, const char *dates); + void (* dates_changed) (CompEditorPage *page, const char *dates); /* Virtual methods */ @@ -78,13 +80,14 @@ void comp_editor_page_fill_widgets (CompEditorPage *page, CalComponent *comp); void comp_editor_page_fill_component (CompEditorPage *page, CalComponent *comp); +void comp_editor_page_set_cal_client (CompEditorPage *page, + CalClient *client); void comp_editor_page_set_summary (CompEditorPage *page, const char *summary); void comp_editor_page_set_dates (CompEditorPage *page, CompEditorPageDates *dates); -void comp_editor_page_set_cal_client (CompEditorPage *page, - CalClient *client); void comp_editor_page_notify_changed (CompEditorPage *page); +void comp_editor_page_notify_needs_send (CompEditorPage *page); void comp_editor_page_notify_summary_changed (CompEditorPage *page, const char *summary); void comp_editor_page_notify_dates_changed (CompEditorPage *page, diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c index 2d894f3ff2..5347a40df3 100644 --- a/calendar/gui/dialogs/comp-editor.c +++ b/calendar/gui/dialogs/comp-editor.c @@ -23,9 +23,18 @@ #include <config.h> #endif +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <gnome.h> +#include <bonobo/bonobo-win.h> +#include <bonobo/bonobo-ui-component.h> +#include <bonobo/bonobo-ui-container.h> +#include <bonobo/bonobo-ui-util.h> #include <gal/widgets/e-unicode.h> #include "save-comp.h" +#include "delete-comp.h" +#include "send-comp.h" #include "comp-editor.h" @@ -43,11 +52,15 @@ struct _CompEditorPrivate { /* Toplevel window for the dialog */ GtkWidget *window; - + BonoboUIComponent *uic; + /* Notebook to hold the pages */ GtkNotebook *notebook; + GtkWidget *filesel; + gboolean changed; + gboolean needs_send; }; @@ -56,15 +69,37 @@ static void comp_editor_class_init (CompEditorClass *class); static void comp_editor_init (CompEditor *editor); static void comp_editor_destroy (GtkObject *object); +static void real_edit_comp (CompEditor *editor, CalComponent *comp); +static void save_comp (CompEditor *editor); +static void delete_comp (CompEditor *editor); +static void close_dialog (CompEditor *editor); + +static void page_changed_cb (GtkWidget *widget, gpointer data); +static void page_needs_send_cb (GtkWidget *widget, gpointer data); static void page_summary_changed_cb (GtkWidget *widget, const char *summary, gpointer data); static void page_dates_changed_cb (GtkWidget *widget, CompEditorPageDates *dates, gpointer data); -static void page_changed_cb (GtkWidget *widget, gpointer data); + +static void save_close_cmd (GtkWidget *widget, gpointer data); +static void save_as_cmd (GtkWidget *widget, gpointer data); +static void delete_cmd (GtkWidget *widget, gpointer data); +static void close_cmd (GtkWidget *widget, gpointer data); static void save_clicked_cb (GtkWidget *widget, gpointer data); static void close_clicked_cb (GtkWidget *widget, gpointer data); static void help_clicked_cb (GtkWidget *widget, gpointer data); static gint delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data); +static BonoboUIVerb verbs [] = { + BONOBO_UI_UNSAFE_VERB ("FileSaveAndClose", save_close_cmd), + BONOBO_UI_UNSAFE_VERB ("FileSaveAs", save_as_cmd), + BONOBO_UI_UNSAFE_VERB ("FileDelete", delete_cmd), + BONOBO_UI_UNSAFE_VERB ("FileClose", close_cmd), + + BONOBO_UI_VERB_END +}; + +#define CLASS(page) (COMP_EDITOR_CLASS (GTK_OBJECT (page)->klass)) + static GtkObjectClass *parent_class; @@ -95,14 +130,16 @@ comp_editor_get_type (void) /* Class initialization function for the calendar component editor */ static void -comp_editor_class_init (CompEditorClass *class) +comp_editor_class_init (CompEditorClass *klass) { GtkObjectClass *object_class; - object_class = (GtkObjectClass *) class; + object_class = (GtkObjectClass *) klass; parent_class = gtk_type_class (GTK_TYPE_OBJECT); + klass->edit_comp = real_edit_comp; + object_class->destroy = comp_editor_destroy; } @@ -111,6 +148,7 @@ static void setup_widgets (CompEditor *editor) { CompEditorPrivate *priv; + BonoboUIContainer *container; GtkWidget *vbox; GtkWidget *bbox; GtkWidget *pixmap; @@ -119,23 +157,29 @@ setup_widgets (CompEditor *editor) priv = editor->priv; /* Window and basic vbox */ - - priv->window = gtk_window_new (GTK_WINDOW_DIALOG); + priv->window = bonobo_window_new ("event-editor", "iCalendar Editor"); gtk_signal_connect (GTK_OBJECT (priv->window), "delete_event", GTK_SIGNAL_FUNC (delete_event_cb), editor); + priv->uic = bonobo_ui_component_new ("comp-editor"); + container = bonobo_ui_container_new (); + bonobo_ui_container_set_win (container, BONOBO_WINDOW (priv->window)); + bonobo_ui_component_set_container (priv->uic, BONOBO_OBJREF (container)); + + bonobo_ui_component_add_verb_list_with_data (priv->uic, verbs, editor); + bonobo_ui_util_set_ui (priv->uic, EVOLUTION_DATADIR "/gnome/gui", + "evolution-comp-editor.xml", "evolution-calendar"); + vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL); gtk_container_set_border_width (GTK_CONTAINER (vbox), GNOME_PAD_SMALL); - gtk_container_add (GTK_CONTAINER (priv->window), vbox); + bonobo_window_set_contents (BONOBO_WINDOW (priv->window), vbox); /* Notebook */ - priv->notebook = GTK_NOTEBOOK (gtk_notebook_new ()); gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (priv->notebook), TRUE, TRUE, 0); /* Buttons */ - bbox = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); @@ -170,6 +214,7 @@ comp_editor_init (CompEditor *editor) priv->pages = NULL; priv->changed = FALSE; + priv->needs_send = FALSE; } /* Destroy handler for the calendar component editor */ @@ -199,7 +244,7 @@ comp_editor_destroy (GtkObject *object) /** * comp_editor_append_page: * @editor: A component editor - * @page: Top level widget of the page + * @page: A component editor page * @label: Label of the page * * Appends a page to the editor notebook with the given label @@ -212,7 +257,7 @@ comp_editor_append_page (CompEditor *editor, CompEditorPrivate *priv; GtkWidget *page_widget; GtkWidget *label_widget; - + g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); g_return_if_fail (page != NULL); @@ -221,9 +266,15 @@ comp_editor_append_page (CompEditor *editor, priv = editor->priv; - /* Only allow adding the pages while a component has not been set */ - g_return_if_fail (priv->comp == NULL); - + /* If we are editing something, fill the widgets with current info */ + if (priv->comp != NULL) { + CalComponent *comp; + + comp = comp_editor_get_current_comp (editor); + comp_editor_page_fill_widgets (page, comp); + gtk_object_unref (GTK_OBJECT (comp)); + } + page_widget = comp_editor_page_get_widget (page); g_assert (page_widget != NULL); @@ -233,12 +284,67 @@ comp_editor_append_page (CompEditor *editor, gtk_notebook_append_page (priv->notebook, page_widget, label_widget); /* Listen for things happening on the page */ + gtk_signal_connect (GTK_OBJECT (page), "needs_send", + GTK_SIGNAL_FUNC (page_needs_send_cb), editor); + gtk_signal_connect (GTK_OBJECT (page), "changed", + GTK_SIGNAL_FUNC (page_changed_cb), editor); gtk_signal_connect (GTK_OBJECT (page), "summary_changed", GTK_SIGNAL_FUNC (page_summary_changed_cb), editor); gtk_signal_connect (GTK_OBJECT (page), "dates_changed", GTK_SIGNAL_FUNC (page_dates_changed_cb), editor); - gtk_signal_connect (GTK_OBJECT (page), "changed", - GTK_SIGNAL_FUNC (page_changed_cb), editor); + +} + +/** + * comp_editor_remove_page: + * @editor: A component editor + * @page: A component editor page + * + * Removes the page from the component editor + **/ +void +comp_editor_remove_page (CompEditor *editor, CompEditorPage *page) +{ + CompEditorPrivate *priv; + GtkWidget *page_widget; + gint page_num; + + g_return_if_fail (editor != NULL); + g_return_if_fail (IS_COMP_EDITOR (editor)); + g_return_if_fail (page != NULL); + g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); + + priv = editor->priv; + + page_widget = comp_editor_page_get_widget (page); + page_num = gtk_notebook_page_num (priv->notebook, page_widget); + gtk_notebook_remove_page (priv->notebook, page_num); +} + +/** + * comp_editor_show_page: + * @editor: + * @page: + * + * + **/ +void +comp_editor_show_page (CompEditor *editor, CompEditorPage *page) +{ + CompEditorPrivate *priv; + GtkWidget *page_widget; + gint page_num; + + g_return_if_fail (editor != NULL); + g_return_if_fail (IS_COMP_EDITOR (editor)); + g_return_if_fail (page != NULL); + g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); + + priv = editor->priv; + + page_widget = comp_editor_page_get_widget (page); + page_num = gtk_notebook_page_num (priv->notebook, page_widget); + gtk_notebook_set_page (priv->notebook, page_num); } /** @@ -368,15 +474,8 @@ fill_widgets (CompEditor *editor) comp_editor_page_fill_widgets (l->data, priv->comp); } -/** - * comp_editor_edit_comp: - * @editor: A component editor - * @comp: A calendar component - * - * Starts the editor editing the given component - **/ -void -comp_editor_edit_comp (CompEditor *editor, CalComponent *comp) +static void +real_edit_comp (CompEditor *editor, CalComponent *comp) { CompEditorPrivate *priv; @@ -394,9 +493,124 @@ comp_editor_edit_comp (CompEditor *editor, CalComponent *comp) priv->comp = cal_component_clone (comp); set_title_from_comp (editor); - fill_widgets (editor); + fill_widgets (editor); } + +/** + * comp_editor_edit_comp: + * @editor: A component editor + * @comp: A calendar component + * + * Starts the editor editing the given component + **/ +void +comp_editor_edit_comp (CompEditor *editor, CalComponent *comp) +{ + CompEditorClass *klass; + + g_return_if_fail (editor != NULL); + g_return_if_fail (IS_COMP_EDITOR (editor)); + g_return_if_fail (comp != NULL); + g_return_if_fail (IS_CAL_COMPONENT (comp)); + + klass = COMP_EDITOR_CLASS (GTK_OBJECT (editor)->klass); + + if (klass->edit_comp) + klass->edit_comp (editor, comp); +} + +CalComponent * +comp_editor_get_current_comp (CompEditor *editor) +{ + CompEditorPrivate *priv; + CalComponent *comp; + GList *l; + + g_return_val_if_fail (editor != NULL, NULL); + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + + priv = editor->priv; + + comp = cal_component_clone (priv->comp); + for (l = priv->pages; l != NULL; l = l->next) + comp_editor_page_fill_component (l->data, comp); + + return comp; +} + +/** + * comp_editor_save_comp: + * @editor: + * + * + **/ +void +comp_editor_save_comp (CompEditor *editor) +{ + save_comp (editor); +} + +/** + * comp_editor_delete_comp: + * @editor: + * + * + **/ +void +comp_editor_delete_comp (CompEditor *editor) +{ + delete_comp (editor); +} + +/** + * comp_editor_send_comp: + * @editor: + * @method: + * + * + **/ +void +comp_editor_send_comp (CompEditor *editor, CalComponentItipMethod method) +{ + CalComponent *comp; + + comp = comp_editor_get_current_comp (editor); + + cal_component_commit_sequence (comp); + itip_send_comp (method, comp); + + gtk_object_unref (GTK_OBJECT (comp)); +} + +/** + * comp_editor_merge_ui: + * @editor: + * @filename: + * @verbs: + * + * + **/ +void +comp_editor_merge_ui (CompEditor *editor, const char *filename, BonoboUIVerb *verbs) +{ + CompEditorPrivate *priv; + BonoboUIEngine *engine; + BonoboUINode *node; + + g_return_if_fail (editor != NULL); + g_return_if_fail (IS_COMP_EDITOR (editor)); + + priv = editor->priv; + + engine = bonobo_window_get_ui_engine (BONOBO_WINDOW (priv->window)); + node = bonobo_ui_util_new_ui (priv->uic, filename, "", + "evolution-calendar"); + g_assert (node != NULL); + + bonobo_ui_engine_xml_merge_tree (engine, "/", node, "comp-editor"); + bonobo_ui_component_add_verb_list_with_data (priv->uic, verbs, editor); +} /* Brings attention to a window by raising it and giving it focus */ static void @@ -438,17 +652,35 @@ save_comp (CompEditor *editor) for (l = priv->pages; l != NULL; l = l->next) comp_editor_page_fill_component (l->data, priv->comp); + if (priv->needs_send && send_component_dialog (priv->comp)) { + cal_component_commit_sequence (priv->comp); + itip_send_comp (CAL_COMPONENT_METHOD_REQUEST, priv->comp); + } + if (!cal_client_update_object (priv->client, priv->comp)) g_message ("save_comp (): Could not update the object!"); else priv->changed = FALSE; } +static void +delete_comp (CompEditor *editor) +{ + CompEditorPrivate *priv; + const char *uid; + + priv = editor->priv; + + cal_component_get_uid (priv->comp, &uid); + cal_client_remove_object (priv->client, uid); + close_dialog (editor); +} + static gboolean prompt_to_save_changes (CompEditor *editor) { CompEditorPrivate *priv; - + priv = editor->priv; if (!priv->changed) @@ -465,7 +697,6 @@ prompt_to_save_changes (CompEditor *editor) case 2: /* Cancel */ default: return FALSE; - break; } } @@ -482,6 +713,144 @@ close_dialog (CompEditor *editor) gtk_object_destroy (GTK_OBJECT (editor)); } +/* Menu Commands */ +static void +save_close_cmd (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + + save_comp (editor); + close_dialog (editor); +} + +static void +save_as_ok (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + CompEditorPrivate *priv; + char *path; + int fd, ret = 0; + + priv = editor->priv; + + path = gtk_file_selection_get_filename (GTK_FILE_SELECTION (priv->filesel)); + + fd = open (path, O_RDONLY); + if (fd != -1) { + GtkWidget *dlg; + GtkWidget *text; + + close (fd); + + dlg = gnome_dialog_new (_("Overwrite file?"), + GNOME_STOCK_BUTTON_YES, + GNOME_STOCK_BUTTON_NO, + NULL); + text = gtk_label_new (_("A file by that name already exists.\nOverwrite it?")); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dlg)->vbox), text, TRUE, TRUE, 4); + gtk_window_set_policy (GTK_WINDOW (dlg), FALSE, TRUE, FALSE); + gtk_widget_show (text); + + ret = gnome_dialog_run_and_close (GNOME_DIALOG (dlg)); + } + + if (ret == 0) { + gchar *ical_string; + + icalcomponent *top_level; + icalcomponent *icalcomp; + icalproperty *prop; + + top_level = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); + + /* RFC 2445, section 4.7.1 */ + prop = icalproperty_new_calscale ("GREGORIAN"); + icalcomponent_add_property (top_level, prop); + + /* RFC 2445, section 4.7.3 */ + prop = icalproperty_new_prodid ("-//Ximian//NONSGML Evolution Calendar//EN"); + icalcomponent_add_property (top_level, prop); + + /* RFC 2445, section 4.7.4. This is the iCalendar spec version, *NOT* + * the product version! Do not change this! + */ + prop = icalproperty_new_version ("2.0"); + icalcomponent_add_property (top_level, prop); + + icalcomp = cal_component_get_icalcomponent (priv->comp); + g_assert (icalcomp != NULL); + + icalcomponent_add_component (top_level, icalcomp); + + ical_string = icalcomponent_as_ical_string (top_level); + + fd = open (path, O_WRONLY); + if (fd == -1) { + g_warning ("Couldn't save item"); + gtk_main_quit (); + return; + } + + write (fd, ical_string, strlen (ical_string)); + close (fd); + + gtk_main_quit (); + } +} + +static void +save_as_cmd (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + CompEditorPrivate *priv; + GtkFileSelection *fs; + char *path; + + priv = editor->priv; + + fs = GTK_FILE_SELECTION (gtk_file_selection_new (_("Save As..."))); + path = g_strdup_printf ("%s/", g_get_home_dir ()); + gtk_file_selection_set_filename (fs, path); + g_free (path); + + gtk_signal_connect (GTK_OBJECT (fs->ok_button), "clicked", + GTK_SIGNAL_FUNC (save_as_ok), editor); + gtk_signal_connect (GTK_OBJECT (fs->cancel_button), "clicked", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + + priv->filesel = GTK_WIDGET (fs); + gtk_widget_show (priv->filesel); + gtk_grab_add (priv->filesel); + gtk_main (); + + gtk_widget_destroy (priv->filesel); + priv->filesel = NULL; +} + +static void +delete_cmd (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + CompEditorPrivate *priv; + CalComponentVType vtype; + + priv = editor->priv; + + vtype = cal_component_get_vtype (priv->comp); + + if (delete_component_dialog (priv->comp, 1, vtype, priv->window)) + delete_comp (editor); +} + +static void +close_cmd (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + + if (prompt_to_save_changes (editor)) + close_dialog (editor); +} + static void save_clicked_cb (GtkWidget *widget, gpointer data) { @@ -500,12 +869,36 @@ close_clicked_cb (GtkWidget *widget, gpointer data) close_dialog (editor); } +/* Button callbacks */ static void help_clicked_cb (GtkWidget *widget, gpointer data) { } static void +page_changed_cb (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + CompEditorPrivate *priv; + + priv = editor->priv; + + priv->changed = TRUE; +} + +static void +page_needs_send_cb (GtkWidget *widget, gpointer data) +{ + CompEditor *editor = COMP_EDITOR (data); + CompEditorPrivate *priv; + + priv = editor->priv; + + priv->needs_send = TRUE; +} + +/* Page signal callbacks */ +static void page_summary_changed_cb (GtkWidget *widget, const char *summary, gpointer data) { CompEditor *editor = COMP_EDITOR (data); @@ -537,18 +930,6 @@ page_dates_changed_cb (GtkWidget *widget, priv->changed = TRUE; } - -static void -page_changed_cb (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; - - priv = editor->priv; - - priv->changed = TRUE; -} - static gint delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { diff --git a/calendar/gui/dialogs/comp-editor.h b/calendar/gui/dialogs/comp-editor.h index 225abeac3c..cf24a2befb 100644 --- a/calendar/gui/dialogs/comp-editor.h +++ b/calendar/gui/dialogs/comp-editor.h @@ -23,7 +23,10 @@ #define COMP_EDITOR_H #include <gtk/gtk.h> +#include <bonobo/bonobo-ui-engine.h> +#include <bonobo/bonobo-ui-component.h> #include "cal-client.h" +#include "../itip-utils.h" #include "comp-editor-page.h" BEGIN_GNOME_DECLS @@ -47,19 +50,36 @@ typedef struct { typedef struct { GtkObjectClass parent_class; + + /* Virtual functions */ + void (* edit_comp) (CompEditor *page, CalComponent *comp); } CompEditorClass; +GtkType comp_editor_get_type (void); +CompEditor *comp_editor_new (void); +void comp_editor_append_page (CompEditor *editor, + CompEditorPage *page, + const char *label); +void comp_editor_remove_page (CompEditor *editor, + CompEditorPage *page); +void comp_editor_show_page (CompEditor *editor, + CompEditorPage *page); +void comp_editor_set_cal_client (CompEditor *editor, + CalClient *client); +CalClient *comp_editor_get_cal_client (CompEditor *editor); +void comp_editor_edit_comp (CompEditor *ee, + CalComponent *comp); +CalComponent *comp_editor_get_current_comp (CompEditor *editor); +void comp_editor_save_comp (CompEditor *editor); +void comp_editor_delete_comp (CompEditor *editor); +void comp_editor_send_comp (CompEditor *editor, + CalComponentItipMethod method); +void comp_editor_merge_ui (CompEditor *editor, + const char *filename, + BonoboUIVerb *verbs); +void comp_editor_focus (CompEditor *editor); + + -GtkType comp_editor_get_type (void); -CompEditor *comp_editor_new (void); -void comp_editor_append_page (CompEditor *editor, - CompEditorPage *page, - const char *label); -void comp_editor_set_cal_client (CompEditor *editor, - CalClient *client); -CalClient * comp_editor_get_cal_client (CompEditor *editor); -void comp_editor_edit_comp (CompEditor *ee, - CalComponent *comp); -void comp_editor_focus (CompEditor *editor); diff --git a/calendar/gui/dialogs/event-editor.c b/calendar/gui/dialogs/event-editor.c index b53efe12aa..36c06ff1f1 100644 --- a/calendar/gui/dialogs/event-editor.c +++ b/calendar/gui/dialogs/event-editor.c @@ -34,21 +34,41 @@ #include "event-page.h" #include "alarm-page.h" #include "recurrence-page.h" +#include "meeting-page.h" +#include "cancel-comp.h" #include "event-editor.h" struct _EventEditorPrivate { EventPage *event_page; AlarmPage *alarm_page; RecurrencePage *recur_page; + MeetingPage *meet_page; + + gboolean meeting_shown; }; static void event_editor_class_init (EventEditorClass *class); static void event_editor_init (EventEditor *ee); +static void event_editor_edit_comp (CompEditor *editor, CalComponent *comp); static void event_editor_destroy (GtkObject *object); -static CompEditor *parent_class; +static void schedule_meeting_cmd (GtkWidget *widget, gpointer data); +static void refresh_meeting_cmd (GtkWidget *widget, gpointer data); +static void cancel_meeting_cmd (GtkWidget *widget, gpointer data); +static void forward_cmd (GtkWidget *widget, gpointer data); + +static BonoboUIVerb verbs [] = { + BONOBO_UI_UNSAFE_VERB ("ActionScheduleMeeting", schedule_meeting_cmd), + BONOBO_UI_UNSAFE_VERB ("ActionRefreshMeeting", refresh_meeting_cmd), + BONOBO_UI_UNSAFE_VERB ("ActionCancelMeeting", cancel_meeting_cmd), + BONOBO_UI_UNSAFE_VERB ("ActionForward", forward_cmd), + + BONOBO_UI_VERB_END +}; + +static CompEditorClass *parent_class; @@ -86,14 +106,18 @@ event_editor_get_type (void) /* Class initialization function for the event editor */ static void -event_editor_class_init (EventEditorClass *class) +event_editor_class_init (EventEditorClass *klass) { GtkObjectClass *object_class; - - object_class = (GtkObjectClass *) class; + CompEditorClass *editor_class; + + object_class = (GtkObjectClass *) klass; + editor_class = (CompEditorClass *) klass; parent_class = gtk_type_class (TYPE_COMP_EDITOR); + editor_class->edit_comp = event_editor_edit_comp; + object_class->destroy = event_editor_destroy; } @@ -120,7 +144,37 @@ event_editor_init (EventEditor *ee) comp_editor_append_page (COMP_EDITOR (ee), COMP_EDITOR_PAGE (priv->recur_page), _("Recurrence")); + + priv->meet_page = meeting_page_new (); + comp_editor_append_page (COMP_EDITOR (ee), + COMP_EDITOR_PAGE (priv->meet_page), + _("Meeting")); + + priv->meeting_shown = TRUE; + + comp_editor_merge_ui (COMP_EDITOR (ee), EVOLUTION_DATADIR + "/gnome/ui/evolution-event-editor.xml", + verbs); +} + +static void +event_editor_edit_comp (CompEditor *editor, CalComponent *comp) +{ + EventEditor *ee; + EventEditorPrivate *priv; + GSList *attendees = NULL; + ee = EVENT_EDITOR (editor); + priv = ee->priv; + + cal_component_get_attendee_list (comp, &attendees); + if (attendees == NULL) { + comp_editor_remove_page (editor, COMP_EDITOR_PAGE (priv->meet_page)); + priv->meeting_shown = FALSE; + } + + if (parent_class->edit_comp) + parent_class->edit_comp (editor, comp); } /* Destroy handler for the event editor */ @@ -153,3 +207,52 @@ event_editor_new (void) { return EVENT_EDITOR (gtk_type_new (TYPE_EVENT_EDITOR)); } + +static void +schedule_meeting_cmd (GtkWidget *widget, gpointer data) +{ + EventEditor *ee = EVENT_EDITOR (data); + EventEditorPrivate *priv; + + priv = ee->priv; + + if (!priv->meeting_shown) { + comp_editor_append_page (COMP_EDITOR (ee), + COMP_EDITOR_PAGE (priv->meet_page), + _("Meeting")); + priv->meeting_shown = FALSE; + } + + comp_editor_show_page (COMP_EDITOR (ee), + COMP_EDITOR_PAGE (priv->meet_page)); +} + +static void +refresh_meeting_cmd (GtkWidget *widget, gpointer data) +{ + EventEditor *ee = EVENT_EDITOR (data); + + comp_editor_send_comp (COMP_EDITOR (ee), CAL_COMPONENT_METHOD_REFRESH); +} + +static void +cancel_meeting_cmd (GtkWidget *widget, gpointer data) +{ + EventEditor *ee = EVENT_EDITOR (data); + CalComponent *comp; + + comp = comp_editor_get_current_comp (COMP_EDITOR (ee)); + if (cancel_component_dialog (comp)) { + comp_editor_send_comp (COMP_EDITOR (ee), CAL_COMPONENT_METHOD_CANCEL); + comp_editor_delete_comp (COMP_EDITOR (ee)); + } +} + +static void +forward_cmd (GtkWidget *widget, gpointer data) +{ + EventEditor *ee = EVENT_EDITOR (data); + + comp_editor_send_comp (COMP_EDITOR (ee), CAL_COMPONENT_METHOD_PUBLISH); +} + diff --git a/calendar/gui/dialogs/meeting-page.c b/calendar/gui/dialogs/meeting-page.c index a369646ab4..7932fbd177 100644 --- a/calendar/gui/dialogs/meeting-page.c +++ b/calendar/gui/dialogs/meeting-page.c @@ -27,13 +27,16 @@ #endif #include <liboaf/liboaf.h> -#include <gtk/gtknotebook.h> -#include <gtk/gtkradiobutton.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-widget.h> #include <gtk/gtksignal.h> -#include <gtk/gtktable.h> #include <gtk/gtktogglebutton.h> #include <gtk/gtkvbox.h> #include <glade/glade.h> +#include <gal/e-table/e-cell-combo.h> +#include <gal/e-table/e-cell-text.h> +#include <gal/e-table/e-table-simple.h> +#include <gal/e-table/e-table-scrolled.h> #include <gal/widgets/e-unicode.h> #include <widgets/misc/e-dateedit.h> #include <widgets/meeting-time-sel/e-meeting-time-sel.h> @@ -45,21 +48,72 @@ #define SELECT_NAMES_OAFID "OAFIID:GNOME_Evolution_Addressbook_SelectNames" +#define MEETING_PAGE_TABLE_SPEC \ + "<ETableSpecification click-to-add=\"true\" " \ + " _click-to-add-message=\"Click here to add an attendee\" " \ + " draw-grid=\"true\">" \ + " <ETableColumn model_col= \"0\" _title=\"Attendee\" " \ + " expansion=\"1.0\" minimum_width=\"10\" resizable=\"true\" " \ + " cell=\"string\" compare=\"string\"/>" \ + " <ETableColumn model_col= \"1\" _title=\"Role\" " \ + " expansion=\"1.0\" minimum_width=\"10\" resizable=\"true\" " \ + " cell=\"roleedit\" compare=\"string\"/>" \ + " <ETableColumn model_col= \"2\" _title=\"RSVP\" " \ + " expansion=\"2.0\" minimum_width=\"10\" resizable=\"true\" " \ + " cell=\"rsvpedit\" compare=\"string\"/>" \ + " <ETableColumn model_col= \"3\" _title=\"Status\" " \ + " expansion=\"2.0\" minimum_width=\"10\" resizable=\"true\" " \ + " cell=\"statusedit\" compare=\"string\"/>" \ + " <ETableState>" \ + " <column source=\"0\"/>" \ + " <column source=\"1\"/>" \ + " <column source=\"2\"/>" \ + " <column source=\"3\"/>" \ + " <grouping></grouping>" \ + " </ETableState>" \ + "</ETableSpecification>" + +enum columns { + MEETING_ATTENDEE_COL, + MEETING_ROLE_COL, + MEETING_RSVP_COL, + MEETING_STATUS_COL, + MEETING_COLUMN_COUNT +}; + +struct attendee { + char *address; + char *member; + + CalComponentCUType cutype; + CalComponentRole role; + CalComponentPartStat status; + gboolean rsvp; + + char *delto; + char *delfrom; + char *sentby; + char *cn; + char *language; +}; + /* Private part of the MeetingPage structure */ struct _MeetingPagePrivate { + /* List of attendees */ + GSList *attendees; + /* Glade XML data */ GladeXML *xml; /* Widgets from the Glade file */ GtkWidget *main; - - EMeetingTimeSelector *selector; - GtkWidget *table; - - /* Other widgets */ - GtkWidget *rb1[2]; - GtkWidget *rb2[2]; + GtkWidget *organizer; + GtkWidget *invite; + /* E Table stuff */ + ETableModel *model; + GtkWidget *etable; + /* For handling the invite button */ GNOME_Evolution_Addressbook_SelectNames corba_select_names; @@ -75,8 +129,8 @@ static void meeting_page_destroy (GtkObject *object); static GtkWidget *meeting_page_get_widget (CompEditorPage *page); static void meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp); static void meeting_page_fill_component (CompEditorPage *page, CalComponent *comp); -static void meeting_page_set_summary (CompEditorPage *page, const char *summary); -static void meeting_page_set_dates (CompEditorPage *page, CompEditorPageDates *dates); + +static int row_count (ETableModel *etm, void *data); static CompEditorPageClass *parent_class = NULL; @@ -130,8 +184,8 @@ meeting_page_class_init (MeetingPageClass *class) editor_page_class->get_widget = meeting_page_get_widget; editor_page_class->fill_widgets = meeting_page_fill_widgets; editor_page_class->fill_component = meeting_page_fill_component; - editor_page_class->set_summary = meeting_page_set_summary; - editor_page_class->set_dates = meeting_page_set_dates; + editor_page_class->set_summary = NULL; + editor_page_class->set_dates = NULL; object_class->destroy = meeting_page_destroy; } @@ -148,8 +202,11 @@ meeting_page_init (MeetingPage *mpage) priv->xml = NULL; priv->main = NULL; - priv->selector = NULL; - + priv->invite = NULL; + + priv->model = NULL; + priv->etable = NULL; + priv->updating = FALSE; } @@ -208,8 +265,9 @@ meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp) { MeetingPage *mpage; MeetingPagePrivate *priv; - CalComponentDateTime dtstart, dtend; - struct icaltimetype start, end; + CalComponentOrganizer organizer; + GSList *attendees, *l; + mpage = MEETING_PAGE (page); priv = mpage->priv; @@ -217,25 +275,39 @@ meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp) /* Clean the screen */ clear_widgets (mpage); + + cal_component_get_organizer (comp, &organizer); + e_dialog_editable_set (priv->organizer, organizer.value); + + cal_component_get_attendee_list (comp, &attendees); + for (l = attendees; l != NULL; l = l->next) { + CalComponentAttendee *att = l->data; + struct attendee *attendee = g_new0 (struct attendee, 1); + + attendee->address = g_strdup (att->value); + attendee->member = g_strdup (att->member); + attendee->cutype= att->cutype; + attendee->role = att->role; + attendee->status = att->status; + attendee->rsvp = att->rsvp; + attendee->delto = g_strdup (att->delto); + attendee->delfrom = g_strdup (att->delfrom); + attendee->sentby = g_strdup (att->sentby); + attendee->cn = g_strdup (att->cn); + attendee->language = g_strdup (att->language); + + priv->attendees = g_slist_prepend (priv->attendees, attendee); + + } + priv->attendees = g_slist_reverse (priv->attendees); + cal_component_free_attendee_list (attendees); - /* Selector */ - cal_component_get_dtstart (comp, &dtstart); - cal_component_get_dtend (comp, &dtend); - start = icaltime_as_zone (*dtstart.value, dtstart.tzid); - end = icaltime_as_zone (*dtend.value, dtend.tzid); - e_meeting_time_selector_set_meeting_time (priv->selector, - start.year, - start.month, - start.day, - start.hour, - start.minute, - end.year, - end.month, - end.day, - end.hour, - end.minute); - cal_component_free_datetime (&dtstart); - cal_component_free_datetime (&dtend); + /* Table */ + e_table_model_rows_inserted (priv->model, 0, row_count (priv->model, mpage)); + + /* So the comp editor knows we need to send if anything changes */ + if (priv->attendees != NULL) + comp_editor_page_notify_needs_send (COMP_EDITOR_PAGE (mpage)); priv->updating = FALSE; } @@ -244,66 +316,48 @@ meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp) static void meeting_page_fill_component (CompEditorPage *page, CalComponent *comp) { -#if 0 MeetingPage *mpage; MeetingPagePrivate *priv; - CalComponentDateTime date; - time_t t; - char *url; + CalComponentOrganizer organizer = {NULL, NULL, NULL, NULL}; + GSList *attendees = NULL, *l; + gchar *str; mpage = MEETING_PAGE (page); priv = mpage->priv; - /* Completed Date. */ - date.value = g_new (struct icaltimetype, 1); - date.tzid = NULL; - - t = e_date_edit_get_time (E_DATE_EDIT (priv->completed_date)); - if (t != -1) { - *date.value = icaltime_from_timet (t, FALSE); - cal_component_set_completed (comp, date.value); - } else { - cal_component_set_completed (comp, NULL); + str = e_dialog_editable_get (priv->organizer); + if (str == NULL || strlen (str) == 0) { + if (str != NULL) + g_free (str); + return; } - - g_free (date.value); - - /* URL. */ - url = e_dialog_editable_get (priv->url); - cal_component_set_url (comp, url); - if (url) - g_free (url); -#endif -} - -/* set_summary handler for the task page */ -static void -meeting_page_set_summary (CompEditorPage *page, const char *summary) -{ -#if 0 - MeetingPage *mpage; - MeetingPagePrivate *priv; - gchar *s; - mpage = MEETING_PAGE (page); - priv = mpage->priv; - - s = e_utf8_to_gtk_string (priv->summary, summary); - gtk_label_set_text (GTK_LABEL (priv->summary), s); - g_free (s); -#endif -} - -static void -meeting_page_set_dates (CompEditorPage *page, CompEditorPageDates *dates) -{ - MeetingPage *mpage; - MeetingPagePrivate *priv; - - mpage = MEETING_PAGE (page); - priv = mpage->priv; - -// comp_editor_date_label (dates, priv->date_time); + organizer.value = str; + cal_component_set_organizer (comp, &organizer); + g_free (str); + + for (l = priv->attendees; l != NULL; l = l->next) { + struct attendee *attendee = l->data; + CalComponentAttendee *att = g_new0 (CalComponentAttendee, 1); + + att->value = attendee->address; + att->member = attendee->member; + att->cutype= attendee->cutype; + att->role = attendee->role; + att->status = attendee->status; + att->rsvp = attendee->rsvp; + att->delto = attendee->delto; + att->delfrom = attendee->delfrom; + att->sentby = attendee->sentby; + att->cn = attendee->cn; + att->language = attendee->language; + + attendees = g_slist_prepend (attendees, att); + + } + attendees = g_slist_reverse (attendees); + cal_component_set_attendee_list (comp, attendees); + g_slist_free (attendees); } @@ -323,77 +377,58 @@ get_widgets (MeetingPage *mpage) gtk_widget_ref (priv->main); gtk_widget_unparent (priv->main); - priv->selector = E_MEETING_TIME_SELECTOR (GW ("selector")); - priv->table = GW ("status-table"); + priv->organizer = GW ("organizer"); + priv->invite = GW ("invite"); #undef GW - return (priv->selector - && priv->table); + return (priv->invite + && priv->organizer); } -/* Creates the radio buttons in the given container */ static void -create_radio_buttons (GtkWidget *box, GtkWidget **button1, GtkWidget **button2) +invite_entry_changed (BonoboListener *listener, + char *event_name, + CORBA_any *arg, + CORBA_Environment *ev, + gpointer user_data) { - GtkWidget *rb1, *rb2; - GSList *grp; - - /* The radio buttions to change views */ - rb1 = gtk_radio_button_new_with_label (NULL, - "Show attendee scheduling"); - gtk_widget_show (rb1); - - grp = gtk_radio_button_group (GTK_RADIO_BUTTON (rb1)); - rb2 = gtk_radio_button_new_with_label (grp, "Show attendee status"); - gtk_widget_show (rb2); - - gtk_box_pack_start (GTK_BOX (box), rb1, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (box), rb2, FALSE, FALSE, 0); - - *button1 = rb1; - *button2 = rb2; } -/* Callback used when the start or end date widgets change. We check that the - * start date < end date and we set the "all day task" button as appropriate. - */ static void -date_changed_cb (EDateEdit *dedit, gpointer data) +add_section (GNOME_Evolution_Addressbook_SelectNames corba_select_names, const char *name) { -#if 0 - MeetingPage *mpage; - MeetingPagePrivate *priv; - CompEditorPageDates dates; + Bonobo_Control corba_control; + CORBA_Environment ev; + GtkWidget *control_widget; + BonoboControlFrame *cf; + Bonobo_PropertyBag pb = CORBA_OBJECT_NIL; + CORBA_exception_init (&ev); - mpage = MEETING_PAGE (data); - priv = mpage->priv; + GNOME_Evolution_Addressbook_SelectNames_addSection (corba_select_names, + name, name, &ev); - if (priv->updating) + corba_control = + GNOME_Evolution_Addressbook_SelectNames_getEntryBySection ( + corba_select_names, name, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + CORBA_exception_free (&ev); return; + } - dates.start = 0; - dates.end = 0; - dates.due = 0; - dates.complete = e_date_edit_get_time (E_DATE_EDIT (priv->completed_date)); - - /* Notify upstream */ - comp_editor_page_notify_dates_changed (COMP_EDITOR_PAGE (mpage), &dates); -#endif -} + CORBA_exception_free (&ev); -/* This is called when any field is changed; it notifies upstream. */ -static void -field_changed_cb (GtkWidget *widget, gpointer data) -{ - MeetingPage *mpage; - MeetingPagePrivate *priv; - - mpage = MEETING_PAGE (data); - priv = mpage->priv; - - if (!priv->updating) - comp_editor_page_notify_changed (COMP_EDITOR_PAGE (mpage)); + control_widget = bonobo_widget_new_control_from_objref ( + corba_control, CORBA_OBJECT_NIL); + + cf = bonobo_widget_get_control_frame (BONOBO_WIDGET (control_widget)); + pb = bonobo_control_frame_get_control_property_bag (cf, NULL); + + bonobo_event_source_client_add_listener ( + pb, invite_entry_changed, + "Bonobo/Property:change:entry_changed", + NULL, NULL); } static gboolean @@ -411,6 +446,10 @@ get_select_name_dialog (MeetingPage *mpage) priv->corba_select_names = oaf_activate_from_id (SELECT_NAMES_OAFID, 0, NULL, &ev); + add_section (priv->corba_select_names, "Required Participants"); + add_section (priv->corba_select_names, "Optional Participants"); + add_section (priv->corba_select_names, "Non-Participants"); + /* OAF seems to be broken -- it can return a CORBA_OBJECT_NIL without raising an exception in `ev'. */ if (ev._major != CORBA_NO_EXCEPTION || priv->corba_select_names == CORBA_OBJECT_NIL) { @@ -424,6 +463,20 @@ get_select_name_dialog (MeetingPage *mpage) return TRUE; } +/* This is called when any field is changed; it notifies upstream. */ +static void +field_changed_cb (GtkWidget *widget, gpointer data) +{ + MeetingPage *mpage; + MeetingPagePrivate *priv; + + mpage = MEETING_PAGE (data); + priv = mpage->priv; + + if (!priv->updating) + comp_editor_page_notify_changed (COMP_EDITOR_PAGE (mpage)); +} + /* Function called to invite more people */ static void invite_cb (GtkWidget *widget, gpointer data) @@ -440,64 +493,363 @@ invite_cb (GtkWidget *widget, gpointer data) CORBA_exception_init (&ev); - GNOME_Evolution_Addressbook_SelectNames_addSection ( - priv->corba_select_names, "Required", "Required", &ev); - GNOME_Evolution_Addressbook_SelectNames_activateDialog ( - priv->corba_select_names, "Test", &ev); + priv->corba_select_names, "Required Participants", &ev); CORBA_exception_free (&ev); - - if (!priv->updating) - comp_editor_page_notify_changed (COMP_EDITOR_PAGE (mpage)); } +/* Hooks the widget signals */ static void -page_toggle_cb (GtkWidget *widget, gpointer data) +init_widgets (MeetingPage *mpage) { + MeetingPagePrivate *priv; + + priv = mpage->priv; + + /* Organizer */ + gtk_signal_connect (GTK_OBJECT (priv->organizer), "changed", + GTK_SIGNAL_FUNC (field_changed_cb), mpage); + + /* Invite button */ + gtk_signal_connect (GTK_OBJECT (priv->invite), "clicked", + GTK_SIGNAL_FUNC (invite_cb), mpage); +} + +static CalComponentRole +text_to_role (const char *role) +{ + if (!g_strcasecmp (role, "Chair")) + return CAL_COMPONENT_ROLE_CHAIR; + else if (!g_strcasecmp (role, "Required Participant")) + return CAL_COMPONENT_ROLE_REQUIRED; + else if (!g_strcasecmp (role, "Optional Participant")) + return CAL_COMPONENT_ROLE_OPTIONAL; + else if (!g_strcasecmp (role, "Non-Participant")) + return CAL_COMPONENT_ROLE_NON; + else + return CAL_COMPONENT_ROLE_UNKNOWN; +} + +static char * +role_to_text (CalComponentRole role) +{ + switch (role) { + case CAL_COMPONENT_ROLE_CHAIR: + return "Chair"; + case CAL_COMPONENT_ROLE_REQUIRED: + return "Required Participant"; + case CAL_COMPONENT_ROLE_OPTIONAL: + return "Optional Participant"; + case CAL_COMPONENT_ROLE_NON: + return "Non-Participant"; + default: + return "Unknown"; + } + + return NULL; +} + +static gboolean +text_to_boolean (const char *role) +{ + if (!g_strcasecmp (role, "Yes")) + return TRUE; + else + return FALSE; +} + +static char * +boolean_to_text (gboolean b) +{ + if (b) + return "Yes"; + else + return "No"; +} + +static CalComponentPartStat +text_to_partstat (const char *partstat) +{ + if (!g_strcasecmp (partstat, "Needs Action")) + return CAL_COMPONENT_PARTSTAT_NEEDSACTION; + else if (!g_strcasecmp (partstat, "Accepted")) + return CAL_COMPONENT_PARTSTAT_ACCEPTED; + else if (!g_strcasecmp (partstat, "Declined")) + return CAL_COMPONENT_PARTSTAT_DECLINED; + else if (!g_strcasecmp (partstat, "Tentative")) + return CAL_COMPONENT_PARTSTAT_TENTATIVE; + else if (!g_strcasecmp (partstat, "Delegated")) + return CAL_COMPONENT_PARTSTAT_DELEGATED; + else if (!g_strcasecmp (partstat, "Completed")) + return CAL_COMPONENT_PARTSTAT_COMPLETED; + else if (!g_strcasecmp (partstat, "In Process")) + return CAL_COMPONENT_PARTSTAT_INPROCESS; + else + return CAL_COMPONENT_PARTSTAT_UNKNOWN; +} + +static char * +partstat_to_text (CalComponentPartStat partstat) +{ + switch (partstat) { + case CAL_COMPONENT_PARTSTAT_NEEDSACTION: + return "Needs Action"; + case CAL_COMPONENT_PARTSTAT_ACCEPTED: + return "Accepted"; + case CAL_COMPONENT_PARTSTAT_DECLINED: + return "Declined"; + case CAL_COMPONENT_PARTSTAT_TENTATIVE: + return "Tentative"; + case CAL_COMPONENT_PARTSTAT_DELEGATED: + return "Delegated"; + case CAL_COMPONENT_PARTSTAT_COMPLETED: + return "Completed"; + case CAL_COMPONENT_PARTSTAT_INPROCESS: + return "In Process"; + case CAL_COMPONENT_PARTSTAT_UNKNOWN: + default: + return "Unknown"; + } + + return NULL; +} + +static int +column_count (ETableModel *etm, void *data) +{ + return MEETING_COLUMN_COUNT; +} + +static int +row_count (ETableModel *etm, void *data) +{ + MeetingPage *mpage; + MeetingPagePrivate *priv; + + mpage = MEETING_PAGE (data); + priv = mpage->priv; + + return g_slist_length (priv->attendees); +} + +static void +append_row (ETableModel *etm, ETableModel *model, int row, void *data) +{ MeetingPage *mpage; MeetingPagePrivate *priv; - gboolean active; + struct attendee *attendee; + gint row_cnt; - mpage = MEETING_PAGE (data); + mpage = MEETING_PAGE (data); priv = mpage->priv; - active = GTK_TOGGLE_BUTTON (widget)->active; - if (active) - gtk_notebook_set_page (GTK_NOTEBOOK (priv->main), 0); - else - gtk_notebook_set_page (GTK_NOTEBOOK (priv->main), 1); + attendee = g_new0 (struct attendee, 1); + + attendee->address = g_strdup (e_table_model_value_at (model, MEETING_ATTENDEE_COL, row)); + attendee->role = text_to_role (e_table_model_value_at (model, MEETING_ROLE_COL, row)); + attendee->rsvp = text_to_boolean (e_table_model_value_at (model, MEETING_RSVP_COL, row)); + attendee->status = text_to_partstat (e_table_model_value_at (model, MEETING_STATUS_COL, row)); + + priv->attendees = g_slist_append (priv->attendees, attendee); + + row_cnt = row_count (etm, data) - 1; + e_table_model_row_inserted (E_TABLE_MODEL (etm), row_cnt); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rb1[0]), active); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rb1[1]), !active); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rb2[0]), active); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rb2[1]), !active); + comp_editor_page_notify_needs_send (COMP_EDITOR_PAGE (mpage)); + comp_editor_page_notify_changed (COMP_EDITOR_PAGE (mpage)); } + +static void * +value_at (ETableModel *etm, int col, int row, void *data) +{ + MeetingPage *mpage; + MeetingPagePrivate *priv; + struct attendee *attendee; + + mpage = MEETING_PAGE (data); + priv = mpage->priv; + + attendee = g_slist_nth_data (priv->attendees, row); -/* Hooks the widget signals */ + switch (col) { + case MEETING_ATTENDEE_COL: + return attendee->address; + case MEETING_ROLE_COL: + return role_to_text (attendee->role); + case MEETING_RSVP_COL: + return boolean_to_text (attendee->rsvp); + case MEETING_STATUS_COL: + return partstat_to_text (attendee->status); + } + + return NULL; +} + static void -init_widgets (MeetingPage *mpage) +set_value_at (ETableModel *etm, int col, int row, const void *val, void *data) { + MeetingPage *mpage; MeetingPagePrivate *priv; + struct attendee *attendee; + mpage = MEETING_PAGE (data); priv = mpage->priv; + + attendee = g_slist_nth_data (priv->attendees, row); + + switch (col) { + case MEETING_ATTENDEE_COL: + attendee->address = g_strdup (val); + break; + case MEETING_ROLE_COL: + attendee->role = text_to_role (val); + break; + case MEETING_RSVP_COL: + attendee->rsvp = text_to_boolean (val); + break; + case MEETING_STATUS_COL: + attendee->status = text_to_partstat (val); + break; + } + + if (!priv->updating) { + comp_editor_page_notify_needs_send (COMP_EDITOR_PAGE (mpage)); + comp_editor_page_notify_changed (COMP_EDITOR_PAGE (mpage)); + } +} - /* Selector */ - gtk_signal_connect (GTK_OBJECT (priv->selector->invite_button), - "clicked", GTK_SIGNAL_FUNC (invite_cb), mpage); +static gboolean +is_cell_editable (ETableModel *etm, int col, int row, void *data) +{ + return TRUE; +} - /* Radio buttons */ - gtk_signal_connect (GTK_OBJECT (priv->rb1[0]), "toggled", - GTK_SIGNAL_FUNC (page_toggle_cb), mpage); - gtk_signal_connect (GTK_OBJECT (priv->rb2[0]), "toggled", - GTK_SIGNAL_FUNC (page_toggle_cb), mpage); +static void * +duplicate_value (ETableModel *etm, int col, const void *val, void *data) +{ + return g_strdup (val); +} + +static void +free_value (ETableModel *etm, int col, void *val, void *data) +{ + g_free (val); +} + +static void * +init_value (ETableModel *etm, int col, void *data) +{ + switch (col) { + case MEETING_ATTENDEE_COL: + return g_strdup (""); + case MEETING_ROLE_COL: + return g_strdup ("Required Participant"); + case MEETING_RSVP_COL: + return g_strdup ("Yes"); + case MEETING_STATUS_COL: + return g_strdup ("Needs Action"); + } -#if 0 - /* Completed Date */ - gtk_signal_connect (GTK_OBJECT (priv->completed_date), "changed", - GTK_SIGNAL_FUNC (date_changed_cb), mpage); + return g_strdup (""); +} -#endif +static gboolean +value_is_empty (ETableModel *etm, int col, const void *val, void *data) +{ + if (col == 0) { + if (val && !g_strcasecmp (val, "")) + return TRUE; + else + return FALSE; + } + + return TRUE; +} + +static char * +value_to_string (ETableModel *etm, int col, const void *val, void *data) +{ + return g_strdup (val); +} + +static void +build_etable (MeetingPage *mpage) +{ + MeetingPagePrivate *priv; + ETableExtras *extras; + GList *strings; + ECell *popup_cell, *cell; + + priv = mpage->priv; + + extras = e_table_extras_new (); + + /* For role */ + cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); + popup_cell = e_cell_combo_new (); + e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); + gtk_object_unref (GTK_OBJECT (cell)); + + strings = NULL; + strings = g_list_append (strings, "Chair"); + strings = g_list_append (strings, "Required Participant"); + strings = g_list_append (strings, "Optional Participant"); + strings = g_list_append (strings, "Non-Participant"); + strings = g_list_append (strings, "Unknown"); + + e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); + e_table_extras_add_cell (extras, "roleedit", popup_cell); + + /* For rsvp */ + cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); + popup_cell = e_cell_combo_new (); + e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); + gtk_object_unref (GTK_OBJECT (cell)); + + strings = NULL; + strings = g_list_append (strings, "Yes"); + strings = g_list_append (strings, "No"); + + e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); + e_table_extras_add_cell (extras, "rsvpedit", popup_cell); + + /* For status */ + cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); + popup_cell = e_cell_combo_new (); + e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); + gtk_object_unref (GTK_OBJECT (cell)); + + strings = NULL; + strings = g_list_append (strings, "Needs Action"); + strings = g_list_append (strings, "Accepted"); + strings = g_list_append (strings, "Declined"); + strings = g_list_append (strings, "Tentative"); + strings = g_list_append (strings, "Delegated"); + + e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); + e_table_extras_add_cell (extras, "statusedit", popup_cell); + + + /* The table itself */ + priv->model = e_table_simple_new (column_count, + row_count, + value_at, + set_value_at, + is_cell_editable, + duplicate_value, + free_value, + init_value, + value_is_empty, + value_to_string, + mpage); + gtk_object_set (GTK_OBJECT (priv->model), + "append_row", append_row, + NULL); + + priv->etable = e_table_scrolled_new (priv->model, extras, + MEETING_PAGE_TABLE_SPEC, NULL); + gtk_object_unref (GTK_OBJECT (extras)); } @@ -515,7 +867,6 @@ MeetingPage * meeting_page_construct (MeetingPage *mpage) { MeetingPagePrivate *priv; - GtkWidget *vbox; priv = mpage->priv; @@ -532,20 +883,12 @@ meeting_page_construct (MeetingPage *mpage) "Could not find all widgets in the XML file!"); return NULL; } - - /* Stuff for the second page */ - vbox = gtk_vbox_new (FALSE, 2); - gtk_widget_show (vbox); - gtk_table_attach (GTK_TABLE (priv->table), vbox, 0, 1, 0, 1, - GTK_EXPAND | GTK_FILL, GTK_FILL, 2, 0); - - /* The radio buttions to change views on first page */ - create_radio_buttons (priv->selector->attendees_title_bar_vbox, - &priv->rb1[0], &priv->rb1[1]); - - /* The radio buttions to change views on second page */ - create_radio_buttons (vbox, &priv->rb2[0], &priv->rb2[1]); - + + /* The etable displaying attendees and their status */ + build_etable (mpage); + gtk_widget_show (priv->etable); + gtk_box_pack_start (GTK_BOX (priv->main), priv->etable, TRUE, TRUE, 2); + /* Init the widget signals */ init_widgets (mpage); @@ -573,15 +916,3 @@ meeting_page_new (void) return mpage; } - -GtkWidget *meeting_page_create_selector (void); - -GtkWidget * -meeting_page_create_selector (void) -{ - GtkWidget *sel; - - sel = e_meeting_time_selector_new (); - - return sel; -} diff --git a/calendar/gui/dialogs/meeting-page.glade b/calendar/gui/dialogs/meeting-page.glade index 86e6647b93..4e23c16f67 100644 --- a/calendar/gui/dialogs/meeting-page.glade +++ b/calendar/gui/dialogs/meeting-page.glade @@ -25,60 +25,75 @@ <auto_shrink>False</auto_shrink> <widget> - <class>GtkNotebook</class> + <class>GtkVBox</class> <name>meeting-page</name> - <show_tabs>False</show_tabs> - <show_border>False</show_border> - <tab_pos>GTK_POS_TOP</tab_pos> - <scrollable>False</scrollable> - <tab_hborder>2</tab_hborder> - <tab_vborder>2</tab_vborder> - <popup_enable>False</popup_enable> + <border_width>5</border_width> + <homogeneous>False</homogeneous> + <spacing>4</spacing> <widget> - <class>Custom</class> - <name>selector</name> - <creation_function>meeting_page_create_selector</creation_function> - <int1>0</int1> - <int2>0</int2> - <last_modification_time>Mon, 04 Jun 2001 17:53:38 GMT</last_modification_time> - </widget> + <class>GtkHBox</class> + <name>hbox1</name> + <homogeneous>False</homogeneous> + <spacing>4</spacing> + <child> + <padding>0</padding> + <expand>False</expand> + <fill>False</fill> + </child> - <widget> - <class>GtkLabel</class> - <child_name>Notebook:tab</child_name> - <name>label1</name> - <label>label1</label> - <justify>GTK_JUSTIFY_CENTER</justify> - <wrap>False</wrap> - <xalign>0.5</xalign> - <yalign>0.5</yalign> - <xpad>0</xpad> - <ypad>0</ypad> + <widget> + <class>GtkLabel</class> + <name>label1</name> + <label>Organizer</label> + <justify>GTK_JUSTIFY_CENTER</justify> + <wrap>False</wrap> + <xalign>0.5</xalign> + <yalign>0.5</yalign> + <xpad>0</xpad> + <ypad>0</ypad> + <child> + <padding>0</padding> + <expand>False</expand> + <fill>False</fill> + </child> + </widget> + + <widget> + <class>GtkEntry</class> + <name>organizer</name> + <can_focus>True</can_focus> + <editable>True</editable> + <text_visible>True</text_visible> + <text_max_length>0</text_max_length> + <text></text> + <child> + <padding>0</padding> + <expand>True</expand> + <fill>True</fill> + </child> + </widget> + + <widget> + <class>Placeholder</class> + </widget> </widget> <widget> - <class>GtkTable</class> - <name>status-table</name> - <border_width>2</border_width> - <rows>2</rows> - <columns>2</columns> - <homogeneous>False</homogeneous> - <row_spacing>0</row_spacing> - <column_spacing>0</column_spacing> + <class>Placeholder</class> </widget> <widget> - <class>GtkLabel</class> - <child_name>Notebook:tab</child_name> - <name>label2</name> - <label>label2</label> - <justify>GTK_JUSTIFY_CENTER</justify> - <wrap>False</wrap> - <xalign>0.5</xalign> - <yalign>0.5</yalign> - <xpad>0</xpad> - <ypad>0</ypad> + <class>GtkButton</class> + <name>invite</name> + <can_focus>True</can_focus> + <label>Invite Others</label> + <child> + <padding>0</padding> + <expand>False</expand> + <fill>False</fill> + <pack>GTK_PACK_END</pack> + </child> </widget> </widget> </widget> diff --git a/calendar/gui/dialogs/recurrence-page.c b/calendar/gui/dialogs/recurrence-page.c index 77b22a97ac..c5c45def15 100644 --- a/calendar/gui/dialogs/recurrence-page.c +++ b/calendar/gui/dialogs/recurrence-page.c @@ -1726,10 +1726,12 @@ recurrence_page_set_dates (CompEditorPage *page, CompEditorPageDates *dates) priv->weekday_day_mask = priv->weekday_day_mask | mask; priv->weekday_blocked_day_mask = mask; - weekday_picker_set_days (priv->weekday_picker, - priv->weekday_day_mask); - weekday_picker_set_blocked_days (priv->weekday_picker, - priv->weekday_blocked_day_mask); + if (priv->weekday_picker != NULL) { + weekday_picker_set_days (WEEKDAY_PICKER (priv->weekday_picker), + priv->weekday_day_mask); + weekday_picker_set_blocked_days (WEEKDAY_PICKER (priv->weekday_picker), + priv->weekday_blocked_day_mask); + } } diff --git a/calendar/gui/dialogs/send-comp.c b/calendar/gui/dialogs/send-comp.c new file mode 100644 index 0000000000..8b1824866c --- /dev/null +++ b/calendar/gui/dialogs/send-comp.c @@ -0,0 +1,84 @@ +/* Evolution calendar - Send calendar component dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Author: JP Rosevear <jpr@ximian.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-i18n.h> +#include <libgnomeui/gnome-dialog.h> +#include <libgnomeui/gnome-dialog-util.h> +#include <libgnomeui/gnome-uidefs.h> +#include <gal/widgets/e-unicode.h> +#include "send-comp.h" + + + +/** + * send_component_dialog: + * + * Pops up a dialog box asking the user whether he wants to send a + * iTip/iMip message + * + * Return value: TRUE if the user clicked Yes, FALSE otherwise. + **/ +gboolean +send_component_dialog (CalComponent *comp) +{ + GtkWidget *dialog; + CalComponentVType vtype; + char *str; + + str = _("The meeting status has changed. Send an updated version?"); + + vtype = cal_component_get_vtype (comp); + + switch (vtype) { + case CAL_COMPONENT_EVENT: + str = g_strdup_printf (_("The meeting information has changed. " + "Send an updated version?")); + break; + + case CAL_COMPONENT_TODO: + str = g_strdup_printf (_("The task information has changed. " + "Send an updated version?")); + break; + + case CAL_COMPONENT_JOURNAL: + str = g_strdup_printf (_("The journal entry has changed. " + "Send an updated version?")); + break; + + default: + g_message ("send_component_dialog(): " + "Cannot handle object of type %d", vtype); + return FALSE; + } + + dialog = gnome_question_dialog_modal (str, NULL, NULL); + + if (gnome_dialog_run (GNOME_DIALOG (dialog)) == GNOME_YES) + return TRUE; + else + return FALSE; +} diff --git a/calendar/gui/dialogs/send-comp.h b/calendar/gui/dialogs/send-comp.h new file mode 100644 index 0000000000..f60ab947aa --- /dev/null +++ b/calendar/gui/dialogs/send-comp.h @@ -0,0 +1,30 @@ +/* Evolution calendar - Send calendar component dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Author: JP Rosevear <jpr@ximian.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SEND_COMP_H +#define SEND_COMP_H + +#include <glib.h> +#include <cal-util/cal-component.h> + +gboolean send_component_dialog (CalComponent *comp); + +#endif diff --git a/calendar/gui/e-itip-control.c b/calendar/gui/e-itip-control.c index dee4bb64e6..cbd84c52e9 100644 --- a/calendar/gui/e-itip-control.c +++ b/calendar/gui/e-itip-control.c @@ -42,6 +42,8 @@ #include "e-itip-control.h" #include <cal-util/cal-component.h> #include <cal-client/cal-client.h> +#include <e-util/e-time-utils.h> +#include "calendar-config.h" #include "itip-utils.h" #define MAIL_COMPOSER_OAF_IID "OAFIID:GNOME_Evolution_Mail_Composer" @@ -235,149 +237,12 @@ change_my_status (icalparameter_partstat status, EItipControlPrivate *priv) } static void -send_itip_reply (EItipControlPrivate *priv) -{ - BonoboObjectClient *bonobo_server; - GNOME_Evolution_Composer composer_server; - CORBA_Environment ev; - GNOME_Evolution_Composer_RecipientList *to_list, *cc_list, *bcc_list; - GNOME_Evolution_Composer_Recipient *recipient; - CORBA_char *subject; - CalComponentText caltext; - CORBA_char *content_type, *filename, *description, *attach_data; - CORBA_boolean show_inline; - CORBA_char tempstr[200]; - - CORBA_exception_init (&ev); - - /* First, I obtain an object reference that represents the Composer. */ - bonobo_server = bonobo_object_activate (MAIL_COMPOSER_OAF_IID, 0); - - g_return_if_fail (bonobo_server != NULL); - - composer_server = bonobo_object_corba_objref (BONOBO_OBJECT (bonobo_server)); - - /* Now I have to make a CORBA sequence that represents a recipient list with - one item, for the organizer. */ - to_list = GNOME_Evolution_Composer_RecipientList__alloc (); - to_list->_maximum = 1; - to_list->_length = 1; - to_list->_buffer = CORBA_sequence_GNOME_Evolution_Composer_Recipient_allocbuf (1); - - recipient = &(to_list->_buffer[0]); - recipient->name = CORBA_string_alloc (0); /* FIXME: we may want an actual name here. */ - recipient->name[0] = '\0'; - recipient->address = CORBA_string_alloc (strlen (priv->organizer)); - strcpy (recipient->address, priv->organizer); - - cc_list = GNOME_Evolution_Composer_RecipientList__alloc (); - cc_list->_maximum = cc_list->_length = 0; - bcc_list = GNOME_Evolution_Composer_RecipientList__alloc (); - bcc_list->_maximum = bcc_list->_length = 0; - - cal_component_get_summary (priv->cal_comp, &caltext); - subject = CORBA_string_alloc (strlen (caltext.value)); - strcpy (subject, caltext.value); - - GNOME_Evolution_Composer_setHeaders (composer_server, to_list, cc_list, bcc_list, subject, &ev); - if (ev._major != CORBA_NO_EXCEPTION) { - g_printerr ("gui/e-meeting-edit.c: I couldn't set the composer headers via CORBA! Aagh.\n"); - CORBA_exception_free (&ev); - return; - } - - sprintf (tempstr, "text/calendar;METHOD=REPLY"); - content_type = CORBA_string_alloc (strlen (tempstr)); - strcpy (content_type, tempstr); - filename = CORBA_string_alloc (0); - filename[0] = '\0'; - sprintf (tempstr, "Calendar attachment"); - description = CORBA_string_alloc (strlen (tempstr)); - strcpy (description, tempstr); - show_inline = FALSE; - - /* I need to create an encapsulating iCalendar component, and stuff our reply event - into it. */ - { - icalcomponent *comp; - icalproperty *prop; - icalvalue *value; - gchar *ical_string; - - comp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - - prop = icalproperty_new (ICAL_PRODID_PROPERTY); - value = icalvalue_new_text ("-//HelixCode/Evolution//EN"); - icalproperty_set_value (prop, value); - icalcomponent_add_property (comp, prop); - - prop = icalproperty_new (ICAL_VERSION_PROPERTY); - value = icalvalue_new_text ("2.0"); - icalproperty_set_value (prop, value); - icalcomponent_add_property (comp, prop); - - prop = icalproperty_new (ICAL_METHOD_PROPERTY); - value = icalvalue_new_text ("REPLY"); - icalproperty_set_value (prop, value); - icalcomponent_add_property (comp, prop); - - icalcomponent_remove_component (priv->main_comp, priv->comp); - icalcomponent_add_component (comp, priv->comp); - - ical_string = icalcomponent_as_ical_string (comp); - attach_data = CORBA_string_alloc (strlen (ical_string)); - strcpy (attach_data, ical_string); - - icalcomponent_remove_component (comp, priv->comp); - icalcomponent_add_component (priv->main_comp, priv->comp); - icalcomponent_free (comp); - - } - - GNOME_Evolution_Composer_attachData (composer_server, - content_type, filename, description, - show_inline, attach_data, - &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_printerr ("gui/e-meeting-edit.c: I couldn't attach data to the composer via CORBA! Aagh.\n"); - CORBA_exception_free (&ev); - return; - } - - GNOME_Evolution_Composer_show (composer_server, &ev); - - if (ev._major != CORBA_NO_EXCEPTION) { - g_printerr ("gui/e-meeting-edit.c: I couldn't show the composer via CORBA! Aagh.\n"); - CORBA_exception_free (&ev); - return; - } - - CORBA_exception_free (&ev); - - /* Here is where we free our graciously-allocated memory. */ - if (CORBA_sequence_get_release (to_list) != FALSE) - CORBA_free (to_list->_buffer); - - CORBA_free (to_list); - CORBA_free (cc_list); - CORBA_free (bcc_list); - - CORBA_free (subject); - CORBA_free (content_type); - CORBA_free (filename); - CORBA_free (description); - CORBA_free (attach_data); - -} - -static void accept_button_clicked_cb (GtkWidget *widget, gpointer data) { EItipControlPrivate *priv = data; change_my_status (ICAL_PARTSTAT_ACCEPTED, priv); - send_itip_reply (priv); + itip_send_comp (CAL_COMPONENT_METHOD_REPLY, priv->cal_comp); update_calendar (priv); return; @@ -389,7 +254,7 @@ tentative_button_clicked_cb (GtkWidget *widget, gpointer data) EItipControlPrivate *priv = data; change_my_status (ICAL_PARTSTAT_TENTATIVE, priv); - send_itip_reply (priv); + itip_send_comp (CAL_COMPONENT_METHOD_REPLY, priv->cal_comp); update_calendar (priv); return; @@ -401,7 +266,7 @@ decline_button_clicked_cb (GtkWidget *widget, gpointer data) EItipControlPrivate *priv = data; change_my_status (ICAL_PARTSTAT_DECLINED, priv); - send_itip_reply (priv); + itip_send_comp (CAL_COMPONENT_METHOD_REPLY, priv->cal_comp); return; } @@ -596,6 +461,7 @@ stream_read (Bonobo_Stream stream) length += buffer->_length; CORBA_free (buffer); +#undef READ_CHUNK_SIZE } while (1); CORBA_free (buffer); @@ -616,7 +482,13 @@ pstream_load (BonoboPersistStream *ps, const Bonobo_Stream stream, CORBA_Environment *ev) { EItipControlPrivate *priv = data; - gint pos, length, length2; + CalComponentText text; + CalComponentDateTime datetime; + CalComponentOrganizer organizer; + icalproperty *prop; + GSList *list, *l; + time_t t; + gint pos = 0; icalcompiter iter; icalcomponent_kind comp_kind; char message[256]; @@ -646,11 +518,10 @@ pstream_load (BonoboPersistStream *ps, const Bonobo_Stream stream, iter = icalcomponent_begin_component (priv->main_comp, ICAL_ANY_COMPONENT); priv->comp = icalcompiter_deref (&iter); -#if 0 { FILE *fp; - fp = fopen ("evo.debug", "w"); + fp = fopen ("/tmp/evo.debug", "w"); fputs ("The raw vCalendar data:\n\n", fp); fputs (priv->vcalendar, fp); @@ -663,7 +534,6 @@ pstream_load (BonoboPersistStream *ps, const Bonobo_Stream stream, fclose (fp); } -#endif if (priv->comp == NULL) { g_printerr ("e-itip-control.c: I could not extract a proper component from\n" @@ -690,7 +560,7 @@ pstream_load (BonoboPersistStream *ps, const Bonobo_Stream stream, break; default: /* We don't know what this is, so bail. */ - { + { GtkWidget *dialog; dialog = gnome_warning_dialog(_("I don't recognize this type of calendar component.")); @@ -700,215 +570,194 @@ pstream_load (BonoboPersistStream *ps, const Bonobo_Stream stream, priv->vcalendar = NULL; return; - } + } break; } /* End switch. */ + + /* Fill in the gui */ + cal_component_get_organizer (priv->cal_comp, &organizer); + priv->organizer = g_strdup (organizer.value); + gtk_entry_set_text (GTK_ENTRY (priv->organizer_entry), priv->organizer); + + cal_component_get_summary (priv->cal_comp, &text); + gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), text.value); - /* Okay, good then; now I will pick apart the component to get - all the things I'll show in my control. */ - { - icalproperty *prop; - const char *description, *summary; - const char *new_text; - const char *organizer; - struct icaltimetype dtstart, dtend; - time_t tstart, tend; - - prop = icalcomponent_get_first_property (priv->comp, ICAL_ORGANIZER_PROPERTY); - if (prop) { - organizer = icalproperty_get_organizer (prop); + cal_component_get_description_list (priv->cal_comp, &list); + for (l = list; l != NULL; l = l->next) { + text = *((CalComponentText *)l->data); - /* Here I strip off the "MAILTO:" if it is present. */ - new_text = strchr (organizer, ':'); - if (new_text != NULL) - new_text++; - else - new_text = organizer; - - priv->organizer = g_strdup (new_text); - gtk_entry_set_text (GTK_ENTRY (priv->organizer_entry), new_text); - } - - prop = icalcomponent_get_first_property (priv->comp, ICAL_SUMMARY_PROPERTY); - if (prop) { - summary = icalproperty_get_summary (prop); - gtk_entry_set_text (GTK_ENTRY (priv->summary_entry), summary); - } + gtk_editable_insert_text (GTK_EDITABLE (priv->description_box), + text.value, strlen (text.value), &pos); + } + cal_component_free_text_list (list); + + cal_component_get_dtstart (priv->cal_comp, &datetime); + t = icaltime_as_timet (*datetime.value); + e_time_format_date_and_time (localtime (&t), + calendar_config_get_24_hour_format (), + FALSE, FALSE, message, sizeof (message)); + gtk_label_set_text (GTK_LABEL (priv->dtstart_label), message); + + cal_component_get_dtend (priv->cal_comp, &datetime); + t = icaltime_as_timet (*datetime.value); + e_time_format_date_and_time (localtime (&t), + calendar_config_get_24_hour_format (), + FALSE, FALSE, message, sizeof (message)); + gtk_label_set_text (GTK_LABEL (priv->dtend_label), message); - prop = icalcomponent_get_first_property (priv->comp, ICAL_DESCRIPTION_PROPERTY); - if (prop) { - description = icalproperty_get_summary (prop); + /* Clear out any old-assed text that's been lying around in my message box. */ + gtk_editable_delete_text (GTK_EDITABLE (priv->message_text), 0, -1); - pos = 0; - length = strlen (description); - length2 = strlen (gtk_editable_get_chars - (GTK_EDITABLE (priv->description_box), 0, -1)); - - if (length2 > 0) - gtk_editable_delete_text (GTK_EDITABLE (priv->description_box), 0, length2); +#if 0 + prop = icalcomponent_get_first_property (priv->comp, ICAL_ORGANIZER_PROPERTY); + if (prop) { + organizer = icalproperty_get_organizer (prop); - gtk_editable_insert_text (GTK_EDITABLE (priv->description_box), - description, - length, - &pos); - } - - prop = icalcomponent_get_first_property (priv->comp, ICAL_DTSTART_PROPERTY); - dtstart = icalproperty_get_dtstart (prop); - prop = icalcomponent_get_first_property (priv->comp, ICAL_DTEND_PROPERTY); - dtend = icalproperty_get_dtend (prop); - - tstart = icaltime_as_timet (dtstart); - tend = icaltime_as_timet (dtend); - - gtk_label_set_text (GTK_LABEL (priv->dtstart_label), ctime (&tstart)); - gtk_label_set_text (GTK_LABEL (priv->dtend_label), ctime (&tend)); + /* Here I strip off the "MAILTO:" if it is present. */ + new_text = strchr (organizer, ':'); + if (new_text != NULL) + new_text++; + else + new_text = organizer; - /* Clear out any old-assed text that's been lying around in my message box. */ - gtk_editable_delete_text (GTK_EDITABLE (priv->message_text), 0, -1); + priv->organizer = g_strdup (new_text); + gtk_entry_set_text (GTK_ENTRY (priv->organizer_entry), new_text); + } +#endif - prop = icalcomponent_get_first_property (priv->main_comp, ICAL_METHOD_PROPERTY); - switch (icalproperty_get_method (prop)) { - case ICAL_METHOD_PUBLISH: - { - GtkWidget *button; + prop = icalcomponent_get_first_property (priv->main_comp, ICAL_METHOD_PROPERTY); + switch (icalproperty_get_method (prop)) { + case ICAL_METHOD_PUBLISH: + { + GtkWidget *button; - snprintf (message, 250, "%s has published calendar information, " - "which you can add to your own calendar. " - "No reply is necessary.", - priv->from_address); + snprintf (message, 250, "%s has published calendar information, " + "which you can add to your own calendar. " + "No reply is necessary.", + priv->from_address); - button = gtk_button_new_with_label (_("Add to Calendar")); - gtk_box_pack_start (GTK_BOX (priv->button_box), button, FALSE, FALSE, 3); - gtk_widget_show (button); + button = gtk_button_new_with_label (_("Add to Calendar")); + gtk_box_pack_start (GTK_BOX (priv->button_box), button, FALSE, FALSE, 3); + gtk_widget_show (button); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (add_button_clicked_cb), priv); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (add_button_clicked_cb), priv); - break; - } - case ICAL_METHOD_REQUEST: - { - /* I'll check if I have to rsvp. */ - icalproperty *prop; - icalparameter *param; - int rsvp = FALSE; - - prop = find_attendee (priv->comp, priv->my_address); - if (prop) { - param = get_icalparam_by_type (prop, ICAL_RSVP_PARAMETER); + break; + } + case ICAL_METHOD_REQUEST: + { + /* I'll check if I have to rsvp. */ + icalproperty *prop; + icalparameter *param; + int rsvp = FALSE; + + prop = find_attendee (priv->comp, priv->my_address); + if (prop) { + param = get_icalparam_by_type (prop, ICAL_RSVP_PARAMETER); - if (param) { - if (icalparameter_get_rsvp (param)) - rsvp = TRUE; - } + if (param) { + if (icalparameter_get_rsvp (param)) + rsvp = TRUE; } + } - snprintf (message, 250, "This is a meeting organized by %s, " - "who indicated that you %s RSVP.", - (priv->organizer ? priv->organizer : "an unknown person"), - (rsvp ? "should" : "don't have to") ); + snprintf (message, 250, "This is a meeting organized by %s, " + "who indicated that you %s RSVP.", + (priv->organizer ? priv->organizer : "an unknown person"), + (rsvp ? "should" : "don't have to") ); - if (rsvp) { - GtkWidget *accept_button, *decline_button, *tentative_button; + if (rsvp) { + GtkWidget *accept_button, *decline_button, *tentative_button; - accept_button = gtk_button_new_with_label (_(" Accept ")); - decline_button = gtk_button_new_with_label (_(" Decline ")); - tentative_button = gtk_button_new_with_label (_(" Tentative ")); + accept_button = gtk_button_new_with_label (_(" Accept ")); + decline_button = gtk_button_new_with_label (_(" Decline ")); + tentative_button = gtk_button_new_with_label (_(" Tentative ")); - gtk_box_pack_start (GTK_BOX (priv->button_box), decline_button, FALSE, FALSE, 3); - gtk_box_pack_end (GTK_BOX (priv->button_box), accept_button, FALSE, FALSE, 3); - gtk_box_pack_end (GTK_BOX (priv->button_box), tentative_button, FALSE, FALSE, 3); + gtk_box_pack_start (GTK_BOX (priv->button_box), decline_button, FALSE, FALSE, 3); + gtk_box_pack_end (GTK_BOX (priv->button_box), accept_button, FALSE, FALSE, 3); + gtk_box_pack_end (GTK_BOX (priv->button_box), tentative_button, FALSE, FALSE, 3); - gtk_signal_connect (GTK_OBJECT (accept_button), "clicked", - GTK_SIGNAL_FUNC (accept_button_clicked_cb), priv); - gtk_signal_connect (GTK_OBJECT (tentative_button), "clicked", - GTK_SIGNAL_FUNC (tentative_button_clicked_cb), priv); - gtk_signal_connect (GTK_OBJECT (decline_button), "clicked", - GTK_SIGNAL_FUNC (decline_button_clicked_cb), priv); + gtk_signal_connect (GTK_OBJECT (accept_button), "clicked", + GTK_SIGNAL_FUNC (accept_button_clicked_cb), priv); + gtk_signal_connect (GTK_OBJECT (tentative_button), "clicked", + GTK_SIGNAL_FUNC (tentative_button_clicked_cb), priv); + gtk_signal_connect (GTK_OBJECT (decline_button), "clicked", + GTK_SIGNAL_FUNC (decline_button_clicked_cb), priv); - gtk_widget_show (accept_button); - gtk_widget_show (tentative_button); - gtk_widget_show (decline_button); - } + gtk_widget_show (accept_button); + gtk_widget_show (tentative_button); + gtk_widget_show (decline_button); + } - } - break; - case ICAL_METHOD_REPLY: - { - icalproperty *prop; - icalparameter *param; - gboolean success = FALSE; - - prop = find_attendee (priv->comp, priv->from_address); - if (prop) { - param = get_icalparam_by_type (prop, ICAL_PARTSTAT_PARAMETER); - if (param) { - success = TRUE; - - priv->new_partstat = icalparameter_get_partstat (param); - } - } + } + break; + case ICAL_METHOD_REPLY: + { + icalproperty *prop; + icalparameter *param; + gboolean success = FALSE; + + prop = find_attendee (priv->comp, priv->from_address); + if (prop) { + param = get_icalparam_by_type (prop, ICAL_PARTSTAT_PARAMETER); + if (param) { + success = TRUE; - if (!success) { - snprintf (message, 250, "%s sent a reply to a meeting request, but " - "the reply is not properly formed.", - priv->from_address); + priv->new_partstat = icalparameter_get_partstat (param); } - else { - GtkWidget *button; + } + + if (!success) { + snprintf (message, 250, "%s sent a reply to a meeting request, but " + "the reply is not properly formed.", + priv->from_address); + } + else { + GtkWidget *button; - button = gtk_button_new_with_label (_("Update Calendar")); - gtk_box_pack_start (GTK_BOX (priv->button_box), button, FALSE, FALSE, 3); - gtk_widget_show (button); + button = gtk_button_new_with_label (_("Update Calendar")); + gtk_box_pack_start (GTK_BOX (priv->button_box), button, FALSE, FALSE, 3); + gtk_widget_show (button); - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (update_reply_cb), priv); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (update_reply_cb), priv); - snprintf (message, 250, "%s responded to your request, replying with: %s", - priv->from_address, partstat_values[priv->new_partstat]); - } - - } - break; - case ICAL_METHOD_CANCEL: - { - if (strcmp (priv->organizer, priv->from_address) != 0) { - snprintf (message, 250, "%s sent a cancellation request, but is not " - "the organizer of the meeting.", - priv->from_address); - } - else { - GtkWidget *button; - - button = gtk_button_new_with_label (_("Cancel Meeting")); - gtk_box_pack_start (GTK_BOX (priv->button_box), button, FALSE, FALSE, 3); - gtk_widget_show (button); - - gtk_signal_connect (GTK_OBJECT (button), "clicked", - GTK_SIGNAL_FUNC (cancel_meeting_cb), priv); + snprintf (message, 250, "%s responded to your request, replying with: %s", + priv->from_address, partstat_values[priv->new_partstat]); + } - snprintf (message, 250, "%s sent a cancellation request. You can" - " delete this event from your calendar, if you wish.", - priv->organizer); - } + } + break; + case ICAL_METHOD_CANCEL: + if (strcmp (priv->organizer, priv->from_address) != 0) { + snprintf (message, 250, "%s sent a cancellation request, but is not " + "the organizer of the meeting.", + priv->from_address); + } else { + GtkWidget *button; - } - break; - default: - snprintf (message, 250, "I haven't the slightest notion what this calendar " - "object represents. Sorry."); - } + button = gtk_button_new_with_label (_("Cancel Meeting")); + gtk_box_pack_start (GTK_BOX (priv->button_box), button, FALSE, FALSE, 3); + gtk_widget_show (button); - { - int pos = 0; + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (cancel_meeting_cb), priv); - gtk_editable_insert_text (GTK_EDITABLE (priv->message_text), message, - strlen (message), &pos); + snprintf (message, 250, "%s sent a cancellation request. You can" + " delete this event from your calendar, if you wish.", + priv->organizer); } + break; + default: + snprintf (message, 250, "I haven't the slightest notion what this calendar " + "object represents. Sorry."); } + pos = 0; + gtk_editable_insert_text (GTK_EDITABLE (priv->message_text), + message, strlen (message), &pos); } /* pstream_load */ /* diff --git a/calendar/gui/itip-utils.c b/calendar/gui/itip-utils.c index 5bf442dec9..239a16ae00 100644 --- a/calendar/gui/itip-utils.c +++ b/calendar/gui/itip-utils.c @@ -4,12 +4,46 @@ * * Authors: * Jesse Pavel <jpavel@helixcode.com> + * JP Rosevear <jpr@ximian.com> * - * Copyright 2000, Helix Code, Inc. + * Copyright 2001, Ximian, Inc. */ +#include <bonobo/bonobo-object.h> +#include <bonobo/bonobo-object-client.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-i18n.h> +#include <libgnomeui/gnome-dialog.h> +#include <libgnomeui/gnome-dialog-util.h> +#include <gtk/gtkwidget.h> +#include <ical.h> +#include <Evolution-Composer.h> #include "itip-utils.h" +#define GNOME_EVOLUTION_COMPOSER_OAFIID "OAFIID:GNOME_Evolution_Mail_Composer" + +static gchar *itip_methods[] = { + "PUBLISH", + "REQUEST", + "REPLY", + "ADD", + "CANCEL" + "RERESH" + "COUNTER" + "DECLINECOUNTER" +}; + +static icalproperty_method itip_methods_enum[] = { + ICAL_METHOD_PUBLISH, + ICAL_METHOD_REQUEST, + ICAL_METHOD_REPLY, + ICAL_METHOD_ADD, + ICAL_METHOD_CANCEL, + ICAL_METHOD_REFRESH, + ICAL_METHOD_COUNTER, + ICAL_METHOD_DECLINECOUNTER, +}; + gchar *partstat_values[] = { "Needs action", "Accepted", @@ -45,4 +79,196 @@ get_icalparam_by_type (icalproperty *prop, icalparameter_kind kind) return param; } +static void +error_dialog (gchar *str) +{ + GtkWidget *dlg; + + dlg = gnome_error_dialog (str); + gnome_dialog_run_and_close (GNOME_DIALOG (dlg)); +} + +void +itip_send_comp (CalComponentItipMethod method, CalComponent *comp) +{ + BonoboObjectClient *bonobo_server; + GNOME_Evolution_Composer composer_server; + CORBA_Environment ev; + GSList *attendees, *l; + GNOME_Evolution_Composer_RecipientList *to_list, *cc_list, *bcc_list; + GNOME_Evolution_Composer_Recipient *recipient; + CORBA_char *subject; + gint cntr; + gint len; + CalComponentText caltext; + CalComponentOrganizer organizer; + CORBA_char *content_type, *filename, *description, *attach_data; + CORBA_boolean show_inline; + CORBA_char tempstr[200]; + + CORBA_exception_init (&ev); + + /* First, I obtain an object reference that represents the Composer. */ + bonobo_server = bonobo_object_activate (GNOME_EVOLUTION_COMPOSER_OAFIID, 0); + g_return_if_fail (bonobo_server != NULL); + + composer_server = BONOBO_OBJREF (bonobo_server); + + switch (method) { + case CAL_COMPONENT_METHOD_PUBLISH: + case CAL_COMPONENT_METHOD_REQUEST: + case CAL_COMPONENT_METHOD_CANCEL: + cal_component_get_attendee_list (comp, &attendees); + len = g_slist_length (attendees); + + to_list = GNOME_Evolution_Composer_RecipientList__alloc (); + to_list->_maximum = len; + to_list->_length = len; + to_list->_buffer = CORBA_sequence_GNOME_Evolution_Composer_Recipient_allocbuf (len); + + for (cntr = 0, l = attendees; cntr < len; cntr++, l = l->next) { + CalComponentAttendee *att = l->data; + + recipient = &(to_list->_buffer[cntr]); + if (att->cn) + recipient->name = CORBA_string_dup (att->cn); + else + recipient->name = CORBA_string_dup (""); + recipient->address = CORBA_string_dup (att->value); + } + break; + + case CAL_COMPONENT_METHOD_REPLY: + case CAL_COMPONENT_METHOD_ADD: + case CAL_COMPONENT_METHOD_REFRESH: + case CAL_COMPONENT_METHOD_COUNTER: + case CAL_COMPONENT_METHOD_DECLINECOUNTER: + cal_component_get_organizer (comp, &organizer); + if (organizer.value == NULL) { + error_dialog (_("An organizer must be set.")); + return; + } + + len = 1; + + to_list = GNOME_Evolution_Composer_RecipientList__alloc (); + to_list->_maximum = len; + to_list->_length = len; + to_list->_buffer = CORBA_sequence_GNOME_Evolution_Composer_Recipient_allocbuf (len); + recipient = &(to_list->_buffer[0]); + + if (organizer.cn != NULL) + recipient->name = CORBA_string_dup (organizer.cn); + else + recipient->name = CORBA_string_dup (""); + recipient->address = CORBA_string_dup (organizer.value); + break; + } + + cc_list = GNOME_Evolution_Composer_RecipientList__alloc (); + cc_list->_maximum = cc_list->_length = 0; + bcc_list = GNOME_Evolution_Composer_RecipientList__alloc (); + bcc_list->_maximum = bcc_list->_length = 0; + + cal_component_get_summary (comp, &caltext); + subject = CORBA_string_dup (caltext.value); + + GNOME_Evolution_Composer_setHeaders (composer_server, to_list, cc_list, bcc_list, subject, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Unable to set composer headers while sending iTip message"); + CORBA_exception_free (&ev); + return; + } + + sprintf (tempstr, "text/calendar;METHOD=%s", itip_methods[method]); + content_type = CORBA_string_dup (tempstr); + filename = CORBA_string_dup (""); + sprintf (tempstr, "Calendar attachment"); + description = CORBA_string_dup (tempstr); + show_inline = FALSE; + + /* I need to create an encapsulating iCalendar component, and stuff our vEvent + into it. */ + { + icalcomponent *icomp, *clone; + icalproperty *prop; + icalvalue *value; + gchar *ical_string; + + icomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); + + prop = icalproperty_new (ICAL_PRODID_PROPERTY); + value = icalvalue_new_text ("-//Ximian/Evolution//EN"); + icalproperty_set_value (prop, value); + icalcomponent_add_property (icomp, prop); + + prop = icalproperty_new (ICAL_VERSION_PROPERTY); + value = icalvalue_new_text ("2.0"); + icalproperty_set_value (prop, value); + icalcomponent_add_property (icomp, prop); + + prop = icalproperty_new (ICAL_METHOD_PROPERTY); + value = icalvalue_new_method (itip_methods_enum[method]); + icalproperty_set_value (prop, value); + icalcomponent_add_property (icomp, prop); + + clone = icalcomponent_new_clone (cal_component_get_icalcomponent (comp)); + icalcomponent_add_component (icomp, clone); + + ical_string = icalcomponent_as_ical_string (icomp); + attach_data = CORBA_string_dup (ical_string); + + icalcomponent_free (icomp); + } + + GNOME_Evolution_Composer_attachData (composer_server, + content_type, filename, description, + show_inline, attach_data, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Unable to attach data to the composer while sending iTip message"); + CORBA_exception_free (&ev); + return; + } + + GNOME_Evolution_Composer_show (composer_server, &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Unable to show the composer while sending iTip message"); + CORBA_exception_free (&ev); + return; + } + + CORBA_exception_free (&ev); + + /* Let's free shit up. */ + + /* Beware--depending on whether CORBA_free is recursive, which I + think is is, we might have memory leaks, in which case the code + below is necessary. */ +#if 0 + for (cntr = 0; cntr < priv->numentries; cntr++) { + recipient = &(to_list->_buffer[cntr]); + CORBA_free (recipient->name); + CORBA_free (recipient->address); + recipient->name = recipient->address = NULL; + } +#endif + + if (CORBA_sequence_get_release (to_list) != FALSE) + CORBA_free (to_list->_buffer); + + CORBA_free (to_list); + CORBA_free (cc_list); + CORBA_free (bcc_list); + + CORBA_free (subject); + CORBA_free (content_type); + CORBA_free (filename); + CORBA_free (description); + CORBA_free (attach_data); + + /* bonobo_object_unref (BONOBO_OBJECT (bonobo_server)); */ +} diff --git a/calendar/gui/itip-utils.h b/calendar/gui/itip-utils.h index a2f825be8b..b51696876f 100644 --- a/calendar/gui/itip-utils.h +++ b/calendar/gui/itip-utils.h @@ -6,10 +6,24 @@ #include <ical.h> #include <string.h> #include <glib.h> +#include <cal-util/cal-component.h> extern gchar *partstat_values[]; extern gchar *role_values[]; icalparameter * get_icalparam_by_type (icalproperty *prop, icalparameter_kind kind); +typedef enum { + CAL_COMPONENT_METHOD_PUBLISH, + CAL_COMPONENT_METHOD_REQUEST, + CAL_COMPONENT_METHOD_REPLY, + CAL_COMPONENT_METHOD_ADD, + CAL_COMPONENT_METHOD_CANCEL, + CAL_COMPONENT_METHOD_REFRESH, + CAL_COMPONENT_METHOD_COUNTER, + CAL_COMPONENT_METHOD_DECLINECOUNTER +} CalComponentItipMethod; + +void itip_send_comp (CalComponentItipMethod method, CalComponent *comp); + #endif |