/* Evolution calendar - Meeting editor dialog * * Copyright (C) 2000 Helix Code, Inc. * Copyright (C) 2000 Ximian, Inc. * * Authors: Jesse Pavel * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "e-meeting-edit.h" #include "itip-utils.h" #define E_MEETING_GLADE_XML "e-meeting-dialog.glade" typedef struct _EMeetingEditorPrivate EMeetingEditorPrivate; struct _EMeetingEditorPrivate { /* These are the widgets to be used in the GUI. */ GladeXML *xml; GtkWidget *meeting_window; GtkWidget *attendee_list; GtkWidget *address_entry; GtkWidget *edit_dialog; GtkWidget *organizer_entry; GtkWidget *role_entry; GtkWidget *rsvp_check; GtkWidget *publish_button, *request_button, *cancel_button, *schedule_button; gint changed_signal_id; /* Various pieces of information. */ gint selected_row; CalComponent *comp; CalClient *client; icalcomponent *icalcomp, *vevent; gint numentries; /* How many attendees are there? */ gboolean dirty; /* Has anything changed? */ }; #define NUM_COLUMNS 4 /* The number of columns in our attendee list. */ enum column_names {ADDRESS_COL, ROLE_COL, RSVP_COL, STATUS_COL}; static void save_organizer (EMeetingEditorPrivate *priv) { icalproperty *prop; icalvalue *value; gchar *text; /* Save the organizer into the iCAL object. */ text = gtk_entry_get_text (GTK_ENTRY (priv->organizer_entry)); if (strlen (text) > 0) { gchar buffer[200]; g_snprintf (buffer, 190, "MAILTO:%s", text); prop = icalcomponent_get_first_property (priv->vevent, ICAL_ORGANIZER_PROPERTY); if (prop == NULL) { /* We need to add an ORGANIZER property. */ prop = icalproperty_new (ICAL_ORGANIZER_PROPERTY); icalcomponent_add_property (priv->vevent, prop); } value = icalvalue_new_text (buffer); icalproperty_set_value (prop, value); } } static gboolean window_delete_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; save_organizer (priv); if (priv->dirty == TRUE) { /* FIXME: notify the event editor that our data has changed. For now, I'll just display a dialog box. */ { if (!cal_client_update_object (priv->client, priv->comp)) { GtkWidget *dialog; dialog = gnome_warning_dialog_parented ("I couldn't automatically save this meeting,\n" "so try to save this event manually.", GTK_WINDOW (priv->meeting_window)); gnome_dialog_run (GNOME_DIALOG(dialog)); } } } gtk_entry_set_text (GTK_ENTRY (priv->organizer_entry), ""); return (FALSE); } static void window_destroy_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; gtk_main_quit (); return; } /* put_property_in_list() synchronizes the display of row `rownum' in our attendee list to the values of `prop'. If rownum < 0, then put_property_in_list() will append a new row. If the property doesn't contain certain parameters that we deem necessary, it will add them. */ static void put_property_in_list (icalproperty *prop, gint rownum, gpointer data) { gchar *row_text[NUM_COLUMNS]; gchar *text, *new_text; icalparameter *param; icalvalue *value; icalparameter_role role_val; icalparameter_partstat part_val; gint cntr; EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; value = icalproperty_get_value (prop); if (value != NULL) { text = strdup (icalvalue_as_ical_string (value)); /* Here I strip off the "MAILTO:" if it is present. */ new_text = strchr (text, ':'); if (new_text != NULL) new_text++; else new_text = text; row_text[ADDRESS_COL] = g_strdup (new_text); g_free (text); } param = get_icalparam_by_type (prop, ICAL_ROLE_PARAMETER); if (param == NULL) { param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); icalproperty_add_parameter (prop, param); } role_val = icalparameter_get_role (param); if (role_val < ICAL_ROLE_CHAIR || role_val > ICAL_ROLE_NONPARTICIPANT) role_val = ICAL_ROLE_REQPARTICIPANT; row_text[ROLE_COL] = role_values[role_val - ICAL_ROLE_CHAIR]; param = get_icalparam_by_type (prop, ICAL_RSVP_PARAMETER); if (param == NULL) { param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); icalproperty_add_parameter (prop, param); } if (icalparameter_get_rsvp (param)) row_text[RSVP_COL] = "Y"; else row_text[RSVP_COL] = "N"; param = get_icalparam_by_type (prop, ICAL_PARTSTAT_PARAMETER); if (param == NULL) { param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); icalproperty_add_parameter (prop, param); } part_val = icalparameter_get_partstat (param); if (part_val < ICAL_PARTSTAT_NEEDSACTION || part_val > ICAL_PARTSTAT_INPROCESS) part_val = ICAL_PARTSTAT_NEEDSACTION; row_text[STATUS_COL] = partstat_values [part_val - ICAL_PARTSTAT_NEEDSACTION]; if (rownum < 0) { gtk_clist_append (GTK_CLIST (priv->attendee_list), row_text); gtk_clist_set_row_data (GTK_CLIST (priv->attendee_list), priv->numentries, prop); priv->numentries++; } else { for (cntr = 0; cntr < NUM_COLUMNS; cntr++) { gtk_clist_set_text (GTK_CLIST (priv->attendee_list), rownum, cntr, row_text[cntr]); } } g_free (row_text[ADDRESS_COL]); } /******** * edit_attendee() performs the GUI manipulation and interaction for * editing `prop' and returns TRUE if the user indicated that he wants * to save the new property information. * * Note that it is necessary that the property have parameters of the types * RSVP, PARTSTAT, and ROLE already when passed into this function. ********/ static gboolean edit_attendee (icalproperty *prop, gpointer data) { EMeetingEditorPrivate *priv; gint button_num; gchar *new_text, *text; icalparameter *param; icalvalue *value; gchar buffer[200]; gint cntr; icalparameter_role role_val; gboolean retval; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; g_return_val_if_fail (prop != NULL, FALSE); if (priv->edit_dialog == NULL || priv->address_entry == NULL) { priv->edit_dialog = glade_xml_get_widget (priv->xml, "edit_dialog"); priv->address_entry = glade_xml_get_widget (priv->xml, "address_entry"); gnome_dialog_set_close (GNOME_DIALOG (priv->edit_dialog), TRUE); gnome_dialog_editable_enters (GNOME_DIALOG (priv->edit_dialog), GTK_EDITABLE (priv->address_entry)); gnome_dialog_close_hides (GNOME_DIALOG (priv->edit_dialog), TRUE); gnome_dialog_set_default (GNOME_DIALOG (priv->edit_dialog), 0); } g_return_val_if_fail (priv->edit_dialog != NULL, FALSE); g_return_val_if_fail (priv->address_entry != NULL, FALSE); gtk_widget_realize (priv->edit_dialog); value = icalproperty_get_value (prop); if (value != NULL) { text = strdup (icalvalue_as_ical_string (value)); /* Here I strip off the "MAILTO:" if it is present. */ new_text = strchr (text, ':'); if (new_text != NULL) new_text++; else new_text = text; gtk_entry_set_text (GTK_ENTRY (priv->address_entry), new_text); g_free (text); } else { gtk_entry_set_text (GTK_ENTRY (priv->address_entry), ""); } param = get_icalparam_by_type (prop, ICAL_ROLE_PARAMETER); role_val = icalparameter_get_role (param); if (role_val < ICAL_ROLE_CHAIR || role_val > ICAL_ROLE_NONPARTICIPANT) role_val = ICAL_ROLE_REQPARTICIPANT; text = role_values [role_val - ICAL_ROLE_CHAIR]; gtk_entry_set_text (GTK_ENTRY (priv->role_entry), text); param = get_icalparam_by_type (prop, ICAL_RSVP_PARAMETER); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rsvp_check), icalparameter_get_rsvp (param)); gtk_widget_show (priv->edit_dialog); button_num = gnome_dialog_run (GNOME_DIALOG (priv->edit_dialog)); if (button_num == 0) { /* The user pressed the OK button. */ new_text = gtk_entry_get_text (GTK_ENTRY (priv->address_entry)); g_snprintf (buffer, 190, "MAILTO:%s", new_text); value = icalvalue_new_text (buffer); icalproperty_set_value (prop, value); /* Take care of the ROLE. */ icalproperty_remove_parameter (prop, ICAL_ROLE_PARAMETER); param = NULL; text = gtk_entry_get_text (GTK_ENTRY(priv->role_entry)); for (cntr = 0; cntr < 4; cntr++) { if (strncmp (text, role_values[cntr], 3) == 0) { param = icalparameter_new_role (ICAL_ROLE_CHAIR + cntr); break; } } if (param == NULL) { g_print ("e-meeting-edit.c: edit_attendee() the ROLE param was null.\n"); /* Use this as a default case, if none of the others match. */ param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); } icalproperty_add_parameter (prop, param); /* Now the RSVP. */ icalproperty_remove_parameter (prop, ICAL_RSVP_PARAMETER); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->rsvp_check))) param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); else param = icalparameter_new_rsvp (ICAL_RSVP_FALSE); icalproperty_add_parameter (prop, param); retval = TRUE; } else /* The user didn't say OK. */ retval = FALSE; return retval; } static void schedule_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; EMeetingTimeSelector *mts; EMeetingTimeSelectorAttendeeType type; GtkWidget *dialog; gchar *attendee; gint cntr, row; icalproperty *prop; icalparameter *param; gint button_num; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; gtk_widget_push_visual (gdk_imlib_get_visual ()); gtk_widget_push_colormap (gdk_imlib_get_colormap ()); dialog = gnome_dialog_new ("Schedule Meeting", "Set Time", "Cancel", NULL); gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 400); gtk_window_set_policy (GTK_WINDOW (dialog), FALSE, TRUE, FALSE); mts = (EMeetingTimeSelector *)e_meeting_time_selector_new (); gtk_container_add (GTK_CONTAINER (GNOME_DIALOG (dialog)->vbox), GTK_WIDGET (mts)); gtk_window_add_accel_group (GTK_WINDOW (dialog), E_MEETING_TIME_SELECTOR (mts)->accel_group); gtk_widget_show (GTK_WIDGET (mts)); gtk_widget_pop_visual (); gtk_widget_pop_colormap (); /* Let's stick all the attendees that we have in our clist, into the meeting time widget. */ for (cntr = 0; cntr < priv->numentries; cntr++ ) { gtk_clist_get_text (GTK_CLIST (priv->attendee_list), cntr, ADDRESS_COL, &attendee); row = e_meeting_time_selector_attendee_add (mts, attendee, NULL); prop = (icalproperty *)gtk_clist_get_row_data (GTK_CLIST (priv->attendee_list), cntr); param = get_icalparam_by_type (prop, ICAL_ROLE_PARAMETER); switch (icalparameter_get_role (param)) { case ICAL_ROLE_CHAIR: case ICAL_ROLE_REQPARTICIPANT: type = E_MEETING_TIME_SELECTOR_REQUIRED_PERSON; break; default: type = E_MEETING_TIME_SELECTOR_OPTIONAL_PERSON; } e_meeting_time_selector_attendee_set_type (mts, row, type); } /* I don't want the meeting widget to be destroyed before I can extract information from it; so now the dialog window will just be hidden when the user clicks a button or closes it. */ gnome_dialog_close_hides (GNOME_DIALOG (dialog), TRUE); gnome_dialog_set_close (GNOME_DIALOG (dialog), TRUE); button_num = gnome_dialog_run (GNOME_DIALOG (dialog)); if (button_num == 0) { /* The user clicked "Set Time". */ gint start_year, start_month, start_day, start_hour, start_minute, end_year, end_month, end_day, end_hour, end_minute; CalComponentDateTime cal_dtstart, cal_dtend; e_meeting_time_selector_get_meeting_time (mts, &start_year, &start_month, &start_day, &start_hour, &start_minute, &end_year, &end_month, &end_day, &end_hour, &end_minute); cal_component_get_dtstart (priv->comp, &cal_dtstart); cal_component_get_dtend (priv->comp, &cal_dtend); cal_dtstart.value->second = 0; cal_dtstart.value->minute = start_minute; cal_dtstart.value->hour = start_hour; cal_dtstart.value->day = start_day; cal_dtstart.value->month = start_month; cal_dtstart.value->year = start_year; cal_dtend.value->second = 0; cal_dtend.value->minute = end_minute; cal_dtend.value->hour = end_hour; cal_dtend.value->day = end_day; cal_dtend.value->month = end_month; cal_dtend.value->year = end_year; cal_component_set_dtstart (priv->comp, &cal_dtstart); cal_component_set_dtend (priv->comp, &cal_dtend); cal_component_free_datetime (&cal_dtstart); cal_component_free_datetime (&cal_dtend); priv->dirty = TRUE; } gtk_widget_destroy (GTK_WIDGET (dialog)); return; } #define GNOME_EVOLUTION_COMPOSER_OAFIID "OAFIID:GNOME_Evolution_Mail_Composer" static gchar *itip_methods[] = { "REQUEST", "PUBLISH", "CANCEL" }; enum itip_method_enum { METHOD_REQUEST, METHOD_PUBLISH, METHOD_CANCEL }; typedef enum itip_method_enum itip_method_enum; static void send_calendar_info (itip_method_enum method, EMeetingEditorPrivate *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; gchar *cell_text; CORBA_char *subject; gint cntr; gint len; 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 (GNOME_EVOLUTION_COMPOSER_OAFIID, 0); g_return_if_fail (bonobo_server != NULL); composer_server = bonobo_object_corba_objref (BONOBO_OBJECT (bonobo_server)); /* All right, now I have to convert my list of recipients into one of those CORBA sequences. */ to_list = GNOME_Evolution_Composer_RecipientList__alloc (); to_list->_maximum = priv->numentries; to_list->_length = priv->numentries; to_list->_buffer = CORBA_sequence_GNOME_Evolution_Composer_Recipient_allocbuf (priv->numentries); for (cntr = 0; cntr < priv->numentries; cntr++) { gtk_clist_get_text (GTK_CLIST (priv->attendee_list), cntr, ADDRESS_COL, &cell_text); len = strlen (cell_text); recipient = &(to_list->_buffer[cntr]); recipient->name = CORBA_string_alloc (0); /* FIXME: we may want an actual name here. */ recipient->name[0] = '\0'; recipient->address = CORBA_string_alloc (len); strcpy (recipient->address, cell_text); } 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->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=%s", itip_methods[method]); 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 vEvent into it. */ { icalcomponent *comp; icalproperty *prop; icalvalue *value; gchar *ical_string; save_organizer (priv); 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 (itip_methods[method]); icalproperty_set_value (prop, value); icalcomponent_add_property (comp, prop); icalcomponent_add_component (comp, priv->vevent); 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->vevent); 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); /* 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)); */ } /******** * This routine is called when the publish button is clicked. Duh. * Actually, I'm just testing my commenting macros. ********/ static void publish_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; send_calendar_info (METHOD_PUBLISH, priv); } static void request_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; send_calendar_info (METHOD_REQUEST, priv); } static void cancel_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; send_calendar_info (METHOD_CANCEL, priv); } static void add_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; icalproperty *prop; icalparameter *param; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; prop = icalproperty_new (ICAL_ATTENDEE_PROPERTY); param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); icalproperty_add_parameter (prop, param); param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); icalproperty_add_parameter (prop, param); param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); icalproperty_add_parameter (prop, param); if (edit_attendee (prop, data) == TRUE) { /* Let's add this property to our component and to the CList. */ icalcomponent_add_property (priv->vevent, prop); /* The -1 indicates that we should add a new row. */ put_property_in_list (prop, -1, data); priv->dirty = TRUE; } else { icalproperty_free (prop); } } static void delete_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; if (priv->selected_row < 0) { GtkWidget *dialog; dialog = gnome_warning_dialog_parented ("You must select an entry to delete.", GTK_WINDOW (priv->meeting_window)); gnome_dialog_run (GNOME_DIALOG(dialog)); } else { /* Delete the associated property from the iCAL object. */ icalproperty *prop; prop = (icalproperty *)gtk_clist_get_row_data (GTK_CLIST (priv->attendee_list), priv->selected_row); icalcomponent_remove_property (priv->vevent, prop); icalproperty_free (prop); gtk_clist_remove (GTK_CLIST (priv->attendee_list), priv->selected_row); priv->selected_row = -1; priv->numentries--; priv->dirty = TRUE; } } static void edit_button_clicked_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; if (priv->selected_row < 0) { GtkWidget *dialog; dialog = gnome_warning_dialog_parented ("You must select an entry to edit.", GTK_WINDOW (priv->meeting_window)); gnome_dialog_run (GNOME_DIALOG(dialog)); return; } else { icalproperty *prop, *new_prop; icalparameter *param; icalvalue *value; prop = (icalproperty *)gtk_clist_get_row_data (GTK_CLIST (priv->attendee_list), priv->selected_row); g_assert (prop != NULL); new_prop = icalproperty_new_clone (prop); if (edit_attendee (new_prop, data)) { /* The user hit Okay. */ /*We need to synchronize the old property with the newly edited one.*/ value = icalvalue_new_clone (icalproperty_get_value (new_prop)); icalproperty_set_value (prop, value); icalproperty_remove_parameter (prop, ICAL_ROLE_PARAMETER); icalproperty_remove_parameter (prop, ICAL_RSVP_PARAMETER); icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER); param = icalparameter_new_clone (get_icalparam_by_type (new_prop, ICAL_ROLE_PARAMETER)); g_assert (param != NULL); icalproperty_add_parameter (prop, param); param = icalparameter_new_clone (get_icalparam_by_type (new_prop, ICAL_RSVP_PARAMETER)); g_assert (param != NULL); icalproperty_add_parameter (prop, param); param = icalparameter_new_clone (get_icalparam_by_type (new_prop, ICAL_PARTSTAT_PARAMETER)); g_assert (param != NULL); icalproperty_add_parameter (prop, param); put_property_in_list (prop, priv->selected_row, data); priv->dirty = TRUE; } icalproperty_free (new_prop); } } static void list_row_select_cb (GtkWidget *widget, gint row, gint column, GdkEventButton *event, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; priv->selected_row = row; } static void organizer_changed_cb (GtkWidget *widget, gpointer data) { EMeetingEditorPrivate *priv; priv = (EMeetingEditorPrivate *) ((EMeetingEditor *)data)->priv; gtk_signal_disconnect (GTK_OBJECT (priv->organizer_entry), priv->changed_signal_id); priv->dirty = TRUE; } /* ------------------------------------------------------------ */ /* --------------------- Exported Functions ------------------- */ /* ------------------------------------------------------------ */ EMeetingEditor * e_meeting_editor_new (CalComponent *comp, CalClient *client) { EMeetingEditor *object; EMeetingEditorPrivate *priv; object = (EMeetingEditor *)g_new(EMeetingEditor, 1); priv = (EMeetingEditorPrivate *) g_new0(EMeetingEditorPrivate, 1); priv->selected_row = -1; priv->comp = comp; priv->client = client; priv->icalcomp = cal_component_get_icalcomponent (comp); object->priv = priv; return object; } void e_meeting_editor_free (EMeetingEditor *editor) { if (editor == NULL) return; if (editor->priv != NULL) g_free (editor->priv); g_free (editor); } void e_meeting_edit (EMeetingEditor *editor) { EMeetingEditorPrivate *priv; GtkWidget *add_button, *delete_button, *edit_button; icalproperty *prop; icalvalue *value; gchar *text; g_return_if_fail (editor != NULL); priv = (EMeetingEditorPrivate *)editor->priv; g_return_if_fail (priv != NULL); priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/" E_MEETING_GLADE_XML, NULL); priv->meeting_window = glade_xml_get_widget (priv->xml, "meeting_window"); priv->attendee_list = glade_xml_get_widget (priv->xml, "attendee_list"); priv->role_entry = glade_xml_get_widget (priv->xml, "role_entry"); priv->rsvp_check = glade_xml_get_widget (priv->xml, "rsvp_check"); priv->schedule_button = glade_xml_get_widget (priv->xml, "schedule_button"); priv->publish_button = glade_xml_get_widget (priv->xml, "publish_button"); priv->request_button = glade_xml_get_widget (priv->xml, "request_button"); priv->cancel_button = glade_xml_get_widget (priv->xml, "cancel_button"); gtk_clist_set_column_justification (GTK_CLIST (priv->attendee_list), ROLE_COL, GTK_JUSTIFY_CENTER); gtk_clist_set_column_justification (GTK_CLIST (priv->attendee_list), RSVP_COL, GTK_JUSTIFY_CENTER); gtk_clist_set_column_justification (GTK_CLIST (priv->attendee_list), STATUS_COL, GTK_JUSTIFY_CENTER); gtk_signal_connect (GTK_OBJECT (priv->meeting_window), "delete_event", GTK_SIGNAL_FUNC (window_delete_cb), editor); gtk_signal_connect_after (GTK_OBJECT (priv->meeting_window), "delete_event", GTK_SIGNAL_FUNC (window_destroy_cb), editor); gtk_signal_connect (GTK_OBJECT (priv->meeting_window), "destroy_event", GTK_SIGNAL_FUNC (window_destroy_cb), editor); gtk_signal_connect (GTK_OBJECT (priv->attendee_list), "select_row", GTK_SIGNAL_FUNC (list_row_select_cb), editor); gtk_signal_connect (GTK_OBJECT (priv->schedule_button), "clicked", GTK_SIGNAL_FUNC (schedule_button_clicked_cb), editor); gtk_signal_connect (GTK_OBJECT (priv->publish_button), "clicked", GTK_SIGNAL_FUNC (publish_button_clicked_cb), editor); gtk_signal_connect (GTK_OBJECT (priv->request_button), "clicked", GTK_SIGNAL_FUNC (request_button_clicked_cb), editor); gtk_signal_connect (GTK_OBJECT (priv->cancel_button), "clicked", GTK_SIGNAL_FUNC (cancel_button_clicked_cb), editor); add_button = glade_xml_get_widget (priv->xml, "add_button"); delete_button = glade_xml_get_widget (priv->xml, "delete_button"); edit_button = glade_xml_get_widget (priv->xml, "edit_button"); gtk_signal_connect (GTK_OBJECT (add_button), "clicked", GTK_SIGNAL_FUNC (add_button_clicked_cb), editor); gtk_signal_connect (GTK_OBJECT (delete_button), "clicked", GTK_SIGNAL_FUNC (delete_button_clicked_cb), editor); gtk_signal_connect (GTK_OBJECT (edit_button), "clicked", GTK_SIGNAL_FUNC (edit_button_clicked_cb), editor); priv->organizer_entry = glade_xml_get_widget (priv->xml, "organizer_entry"); if (icalcomponent_isa (priv->icalcomp) != ICAL_VEVENT_COMPONENT) { icalcompiter iter; iter = icalcomponent_begin_component (priv->icalcomp, ICAL_VEVENT_COMPONENT); priv->vevent = icalcompiter_deref (&iter); } else priv->vevent = priv->icalcomp; g_assert (priv->vevent != NULL); /* Let's extract the organizer, if there is one. */ prop = icalcomponent_get_first_property (priv->vevent, ICAL_ORGANIZER_PROPERTY); if (prop != NULL) { gchar *buffer; value = icalproperty_get_value (prop); buffer = g_strdup (icalvalue_as_ical_string (value)); if (buffer != NULL) { /* Strip off the MAILTO:, if it is present. */ text = strchr (buffer, ':'); if (text == NULL) text = buffer; else text++; gtk_entry_set_text (GTK_ENTRY (priv->organizer_entry), text); g_free (buffer); } } priv->changed_signal_id = gtk_signal_connect (GTK_OBJECT (priv->organizer_entry), "changed", GTK_SIGNAL_FUNC (organizer_changed_cb), editor); /* Let's go through the iCAL object, and create a list entry for each ATTENDEE property. */ for (prop = icalcomponent_get_first_property (priv->vevent, ICAL_ATTENDEE_PROPERTY); prop != NULL; prop = icalcomponent_get_next_property (priv->vevent, ICAL_ATTENDEE_PROPERTY)) { put_property_in_list (prop, -1, editor); } gtk_widget_show (priv->meeting_window); gtk_main (); if (priv->meeting_window != NULL) gtk_widget_destroy (priv->meeting_window); if (priv->edit_dialog != NULL) gtk_widget_destroy (priv->edit_dialog); gtk_object_unref (GTK_OBJECT (priv->xml)); }