diff options
Diffstat (limited to 'calendar/gui/e-meeting-model.c')
-rw-r--r-- | calendar/gui/e-meeting-model.c | 1780 |
1 files changed, 1780 insertions, 0 deletions
diff --git a/calendar/gui/e-meeting-model.c b/calendar/gui/e-meeting-model.c new file mode 100644 index 0000000000..4742116cf2 --- /dev/null +++ b/calendar/gui/e-meeting-model.c @@ -0,0 +1,1780 @@ +/* -*- Mod:e C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* itip-model.c + * + * Copyright (C) 2001 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: JP Rosevear + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-widget.h> +#include <bonobo/bonobo-exception.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomevfs/gnome-vfs.h> +#include <gal/e-table/e-table-without.h> +#include <gal/e-table/e-cell-text.h> +#include <gal/e-table/e-cell-popup.h> +#include <gal/e-table/e-cell-combo.h> +#include <addressbook/util/eab-destination.h> +#include <ebook/e-book.h> +#include <ebook/e-book-async.h> +#include <ebook/e-contact.h> +#include <cal-util/cal-component.h> +#include <cal-util/cal-util.h> +#include <cal-util/timeutil.h> +#include "Evolution-Addressbook-SelectNames.h" +#include "calendar-config.h" +#include "itip-utils.h" +#include "e-meeting-utils.h" +#include "e-meeting-attendee.h" +#include "e-meeting-model.h" + +#define SELECT_NAMES_OAFID "OAFIID:GNOME_Evolution_Addressbook_SelectNames" + +struct _EMeetingModelPrivate +{ + GPtrArray *attendees; + + GList *tables; + + CalClient *client; + icaltimezone *zone; + + EBook *ebook; + gboolean book_loaded; + gboolean book_load_wait; + + GPtrArray *refresh_queue; + GHashTable *refresh_data; + guint refresh_idle_id; + + /* For invite others dialogs */ + GNOME_Evolution_Addressbook_SelectNames corba_select_names; +}; + +#define BUF_SIZE 1024 + +static char *sections[] = {N_("Chair Persons"), + N_("Required Participants"), + N_("Optional Participants"), + N_("Resources"), + NULL}; +static icalparameter_role roles[] = {ICAL_ROLE_CHAIR, + ICAL_ROLE_REQPARTICIPANT, + ICAL_ROLE_OPTPARTICIPANT, + ICAL_ROLE_NONPARTICIPANT, + ICAL_ROLE_NONE}; + +typedef struct _EMeetingModelQueueData EMeetingModelQueueData; +struct _EMeetingModelQueueData { + EMeetingModel *im; + EMeetingAttendee *ia; + + gboolean refreshing; + + EMeetingTime start; + EMeetingTime end; + + char buffer[BUF_SIZE]; + GString *string; + + GPtrArray *call_backs; + GPtrArray *data; +}; + + +static void class_init (EMeetingModelClass *klass); +static void init (EMeetingModel *model); +static void finalize (GObject *obj); + +static void refresh_queue_add (EMeetingModel *im, int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingModelRefreshCallback call_back, + gpointer data); +static void refresh_queue_remove (EMeetingModel *im, + EMeetingAttendee *ia); +static gboolean refresh_busy_periods (gpointer data); + +static void attendee_changed_cb (EMeetingAttendee *ia, gpointer data); +static void select_names_ok_cb (BonoboListener *listener, + const char *event_name, + const CORBA_any *arg, + CORBA_Environment *ev, + gpointer data); + +static void table_destroy_state_cb (ETableScrolled *etable, gpointer data); +static void table_destroy_list_cb (ETableScrolled *etable, gpointer data); + +static ETableModelClass *parent_class = NULL; + +E_MAKE_TYPE (e_meeting_model, "EMeetingModel", EMeetingModel, + class_init, init, E_TABLE_MODEL_TYPE); + +static void +book_open_cb (EBook *book, EBookStatus status, gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + + priv = im->priv; + + priv->ebook = book; + + if (status == E_BOOK_ERROR_OK) + priv->book_loaded = TRUE; + else + g_warning ("Book not loaded"); + + if (priv->book_load_wait) { + priv->book_load_wait = FALSE; + gtk_main_quit (); + } +} + +static void +start_addressbook_server (EMeetingModel *im) +{ + e_book_async_get_default_addressbook (book_open_cb, im); +} + +static EMeetingAttendee * +find_match (EMeetingModel *im, const char *address, int *pos) +{ + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + const gchar *ia_address; + int i; + + priv = im->priv; + + if (address == NULL) + return NULL; + + /* Make sure we can add the new delegatee person */ + for (i = 0; i < priv->attendees->len; i++) { + ia = g_ptr_array_index (priv->attendees, i); + + ia_address = e_meeting_attendee_get_address (ia); + if (ia_address != NULL && !g_strcasecmp (itip_strip_mailto (ia_address), itip_strip_mailto (address))) { + if (pos != NULL) + *pos = i; + return ia; + } + } + + return NULL; +} + +static icalparameter_cutype +text_to_type (const char *type) +{ + if (!g_strcasecmp (type, _("Individual"))) + return ICAL_CUTYPE_INDIVIDUAL; + else if (!g_strcasecmp (type, _("Group"))) + return ICAL_CUTYPE_GROUP; + else if (!g_strcasecmp (type, _("Resource"))) + return ICAL_CUTYPE_RESOURCE; + else if (!g_strcasecmp (type, _("Room"))) + return ICAL_CUTYPE_ROOM; + else + return ICAL_CUTYPE_NONE; +} + +static char * +type_to_text (icalparameter_cutype type) +{ + switch (type) { + case ICAL_CUTYPE_INDIVIDUAL: + return _("Individual"); + case ICAL_CUTYPE_GROUP: + return _("Group"); + case ICAL_CUTYPE_RESOURCE: + return _("Resource"); + case ICAL_CUTYPE_ROOM: + return _("Room"); + default: + return _("Unknown"); + } + + return NULL; + +} + +static icalparameter_role +text_to_role (const char *role) +{ + if (!g_strcasecmp (role, _("Chair"))) + return ICAL_ROLE_CHAIR; + else if (!g_strcasecmp (role, _("Required Participant"))) + return ICAL_ROLE_REQPARTICIPANT; + else if (!g_strcasecmp (role, _("Optional Participant"))) + return ICAL_ROLE_OPTPARTICIPANT; + else if (!g_strcasecmp (role, _("Non-Participant"))) + return ICAL_ROLE_NONPARTICIPANT; + else + return ICAL_ROLE_NONE; +} + +static char * +role_to_text (icalparameter_role role) +{ + switch (role) { + case ICAL_ROLE_CHAIR: + return _("Chair"); + case ICAL_ROLE_REQPARTICIPANT: + return _("Required Participant"); + case ICAL_ROLE_OPTPARTICIPANT: + return _("Optional Participant"); + case ICAL_ROLE_NONPARTICIPANT: + 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 icalparameter_partstat +text_to_partstat (const char *partstat) +{ + if (!g_strcasecmp (partstat, _("Needs Action"))) + return ICAL_PARTSTAT_NEEDSACTION; + else if (!g_strcasecmp (partstat, _("Accepted"))) + return ICAL_PARTSTAT_ACCEPTED; + else if (!g_strcasecmp (partstat, _("Declined"))) + return ICAL_PARTSTAT_DECLINED; + else if (!g_strcasecmp (partstat, _("Tentative"))) + return ICAL_PARTSTAT_TENTATIVE; + else if (!g_strcasecmp (partstat, _("Delegated"))) + return ICAL_PARTSTAT_DELEGATED; + else if (!g_strcasecmp (partstat, _("Completed"))) + return ICAL_PARTSTAT_COMPLETED; + else if (!g_strcasecmp (partstat, _("In Process"))) + return ICAL_PARTSTAT_INPROCESS; + else + return ICAL_PARTSTAT_NONE; +} + +static char * +partstat_to_text (icalparameter_partstat partstat) +{ + switch (partstat) { + case ICAL_PARTSTAT_NEEDSACTION: + return _("Needs Action"); + case ICAL_PARTSTAT_ACCEPTED: + return _("Accepted"); + case ICAL_PARTSTAT_DECLINED: + return _("Declined"); + case ICAL_PARTSTAT_TENTATIVE: + return _("Tentative"); + case ICAL_PARTSTAT_DELEGATED: + return _("Delegated"); + case ICAL_PARTSTAT_COMPLETED: + return _("Completed"); + case ICAL_PARTSTAT_INPROCESS: + return _("In Process"); + case ICAL_PARTSTAT_NONE: + default: + return _("Unknown"); + } + + return NULL; +} + +static int +column_count (ETableModel *etm) +{ + return E_MEETING_MODEL_COLUMN_COUNT; +} + +static int +row_count (ETableModel *etm) +{ + EMeetingModel *im; + EMeetingModelPrivate *priv; + + im = E_MEETING_MODEL (etm); + priv = im->priv; + + return (priv->attendees->len); +} + +static void +append_row (ETableModel *etm, ETableModel *source, int row) +{ + EMeetingModel *im; + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + char *address; + + im = E_MEETING_MODEL (etm); + priv = im->priv; + + address = (char *) e_table_model_value_at (source, E_MEETING_MODEL_ADDRESS_COL, row); + if (find_match (im, address, NULL) != NULL) { + return; + } + + ia = E_MEETING_ATTENDEE (e_meeting_attendee_new ()); + + e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", address)); + e_meeting_attendee_set_member (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_MEMBER_COL, row))); + e_meeting_attendee_set_cutype (ia, text_to_type (e_table_model_value_at (source, E_MEETING_MODEL_TYPE_COL, row))); + e_meeting_attendee_set_role (ia, text_to_role (e_table_model_value_at (source, E_MEETING_MODEL_ROLE_COL, row))); + e_meeting_attendee_set_rsvp (ia, text_to_boolean (e_table_model_value_at (source, E_MEETING_MODEL_RSVP_COL, row))); + e_meeting_attendee_set_delto (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_DELTO_COL, row))); + e_meeting_attendee_set_delfrom (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_DELFROM_COL, row))); + e_meeting_attendee_set_status (ia, text_to_partstat (e_table_model_value_at (source, E_MEETING_MODEL_STATUS_COL, row))); + e_meeting_attendee_set_cn (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_CN_COL, row))); + e_meeting_attendee_set_language (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_LANGUAGE_COL, row))); + + e_meeting_model_add_attendee (E_MEETING_MODEL (etm), ia); + g_object_unref (ia); +} + +static void * +value_at (ETableModel *etm, int col, int row) +{ + EMeetingModel *im; + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + + im = E_MEETING_MODEL (etm); + priv = im->priv; + + ia = g_ptr_array_index (priv->attendees, row); + + switch (col) { + case E_MEETING_MODEL_ADDRESS_COL: + return (void *)itip_strip_mailto (e_meeting_attendee_get_address (ia)); + case E_MEETING_MODEL_MEMBER_COL: + return (void *)e_meeting_attendee_get_member (ia); + case E_MEETING_MODEL_TYPE_COL: + return type_to_text (e_meeting_attendee_get_cutype (ia)); + case E_MEETING_MODEL_ROLE_COL: + return role_to_text (e_meeting_attendee_get_role (ia)); + case E_MEETING_MODEL_RSVP_COL: + return boolean_to_text (e_meeting_attendee_get_rsvp (ia)); + case E_MEETING_MODEL_DELTO_COL: + return (void *)itip_strip_mailto (e_meeting_attendee_get_delto (ia)); + case E_MEETING_MODEL_DELFROM_COL: + return (void *)itip_strip_mailto (e_meeting_attendee_get_delfrom (ia)); + case E_MEETING_MODEL_STATUS_COL: + return partstat_to_text (e_meeting_attendee_get_status (ia)); + case E_MEETING_MODEL_CN_COL: + return (void *)e_meeting_attendee_get_cn (ia); + case E_MEETING_MODEL_LANGUAGE_COL: + return (void *)e_meeting_attendee_get_language (ia); + } + + return NULL; +} + +static void +set_value_at (ETableModel *etm, int col, int row, const void *val) +{ + EMeetingModel *im; + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + icalparameter_cutype type; + + im = E_MEETING_MODEL (etm); + priv = im->priv; + + ia = g_ptr_array_index (priv->attendees, row); + + e_table_model_pre_change (etm); + + switch (col) { + case E_MEETING_MODEL_ADDRESS_COL: + if (val != NULL && *((char *)val)) + e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", (char *) val)); + break; + case E_MEETING_MODEL_MEMBER_COL: + e_meeting_attendee_set_member (ia, g_strdup (val)); + break; + case E_MEETING_MODEL_TYPE_COL: + type = text_to_type (val); + e_meeting_attendee_set_cutype (ia, text_to_type (val)); + if (type == ICAL_CUTYPE_RESOURCE) { + e_meeting_attendee_set_role (ia, ICAL_ROLE_NONPARTICIPANT); + e_table_model_cell_changed (etm, E_MEETING_MODEL_ROLE_COL, row); + } + break; + case E_MEETING_MODEL_ROLE_COL: + e_meeting_attendee_set_role (ia, text_to_role (val)); + break; + case E_MEETING_MODEL_RSVP_COL: + e_meeting_attendee_set_rsvp (ia, text_to_boolean (val)); + break; + case E_MEETING_MODEL_DELTO_COL: + e_meeting_attendee_set_delto (ia, g_strdup (val)); + break; + case E_MEETING_MODEL_DELFROM_COL: + e_meeting_attendee_set_delfrom (ia, g_strdup (val)); + break; + case E_MEETING_MODEL_STATUS_COL: + e_meeting_attendee_set_status (ia, text_to_partstat (val)); + break; + case E_MEETING_MODEL_CN_COL: + e_meeting_attendee_set_cn (ia, g_strdup (val)); + break; + case E_MEETING_MODEL_LANGUAGE_COL: + e_meeting_attendee_set_language (ia, g_strdup (val)); + break; + } + + e_table_model_cell_changed (etm, col, row); +} + +static gboolean +is_cell_editable (ETableModel *etm, int col, int row) +{ + EMeetingModel *im; + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + EMeetingAttendeeEditLevel level; + + im = E_MEETING_MODEL (etm); + priv = im->priv; + + if (col == E_MEETING_MODEL_DELTO_COL + || col == E_MEETING_MODEL_DELFROM_COL) + return FALSE; + + if (row == -1) + return TRUE; + if (row >= priv->attendees->len) + return TRUE; + + ia = g_ptr_array_index (priv->attendees, row); + level = e_meeting_attendee_get_edit_level (ia); + + switch (level) { + case E_MEETING_ATTENDEE_EDIT_FULL: + return TRUE; + case E_MEETING_ATTENDEE_EDIT_STATUS: + return col == E_MEETING_MODEL_STATUS_COL; + case E_MEETING_ATTENDEE_EDIT_NONE: + return FALSE; + } + + return TRUE; +} + +static void * +duplicate_value (ETableModel *etm, int col, const void *val) +{ + return g_strdup (val); +} + +static void +free_value (ETableModel *etm, int col, void *val) +{ + g_free (val); +} + +static void * +init_value (ETableModel *etm, int col) +{ + switch (col) { + case E_MEETING_MODEL_ADDRESS_COL: + return g_strdup (""); + case E_MEETING_MODEL_MEMBER_COL: + return g_strdup (""); + case E_MEETING_MODEL_TYPE_COL: + return g_strdup (_("Individual")); + case E_MEETING_MODEL_ROLE_COL: + return g_strdup (_("Required Participant")); + case E_MEETING_MODEL_RSVP_COL: + return g_strdup (_("Yes")); + case E_MEETING_MODEL_DELTO_COL: + return g_strdup (""); + case E_MEETING_MODEL_DELFROM_COL: + return g_strdup (""); + case E_MEETING_MODEL_STATUS_COL: + return g_strdup (_("Needs Action")); + case E_MEETING_MODEL_CN_COL: + return g_strdup (""); + case E_MEETING_MODEL_LANGUAGE_COL: + return g_strdup ("en"); + } + + return g_strdup (""); +} + +static gboolean +value_is_empty (ETableModel *etm, int col, const void *val) +{ + + switch (col) { + case E_MEETING_MODEL_ADDRESS_COL: + case E_MEETING_MODEL_MEMBER_COL: + case E_MEETING_MODEL_DELTO_COL: + case E_MEETING_MODEL_DELFROM_COL: + case E_MEETING_MODEL_CN_COL: + if (val && !g_strcasecmp (val, "")) + return TRUE; + else + return FALSE; + default: + ; + } + + return TRUE; +} + +static char * +value_to_string (ETableModel *etm, int col, const void *val) +{ + return g_strdup (val); +} + +static void +class_init (EMeetingModelClass *klass) +{ + GObjectClass *gobject_class; + ETableModelClass *etm_class; + + gobject_class = G_OBJECT_CLASS (klass); + etm_class = E_TABLE_MODEL_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = finalize; + + etm_class->column_count = column_count; + etm_class->row_count = row_count; + etm_class->value_at = value_at; + etm_class->set_value_at = set_value_at; + etm_class->is_cell_editable = is_cell_editable; + etm_class->append_row = append_row; + etm_class->duplicate_value = duplicate_value; + etm_class->free_value = free_value; + etm_class->initialize_value = init_value; + etm_class->value_is_empty = value_is_empty; + etm_class->value_to_string = value_to_string; +} + + +static void +init (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + + priv = g_new0 (EMeetingModelPrivate, 1); + + im->priv = priv; + + priv->attendees = g_ptr_array_new (); + + priv->tables = NULL; + + priv->client = NULL; + priv->zone = icaltimezone_get_builtin_timezone (calendar_config_get_timezone ()); + + priv->ebook = NULL; + priv->book_loaded = FALSE; + priv->book_load_wait = FALSE; + + priv->refresh_queue = g_ptr_array_new (); + priv->refresh_data = g_hash_table_new (g_direct_hash, g_direct_equal); + priv->refresh_idle_id = 0; + + priv->corba_select_names = CORBA_OBJECT_NIL; + + start_addressbook_server (im); +} + +static void +finalize (GObject *obj) +{ + EMeetingModel *im = E_MEETING_MODEL (obj); + EMeetingModelPrivate *priv; + GList *l; + int i; + + priv = im->priv; + + for (i = 0; i < priv->attendees->len; i++) + g_object_unref (g_ptr_array_index (priv->attendees, i)); + g_ptr_array_free (priv->attendees, TRUE); + + for (l = priv->tables; l != NULL; l = l->next) + g_signal_handlers_disconnect_matched (G_OBJECT (l->data), G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, im); + g_list_free (priv->tables); + + if (priv->client != NULL) + g_object_unref (priv->client); + + if (priv->ebook != NULL) + g_object_unref (priv->ebook); + + if (priv->corba_select_names != CORBA_OBJECT_NIL) { + CORBA_Environment ev; + CORBA_exception_init (&ev); + bonobo_object_release_unref (priv->corba_select_names, &ev); + CORBA_exception_free (&ev); + } + + while (priv->refresh_queue->len > 0) + refresh_queue_remove (im, g_ptr_array_index (priv->refresh_queue, 0)); + g_ptr_array_free (priv->refresh_queue, TRUE); + g_hash_table_destroy (priv->refresh_data); + + if (priv->refresh_idle_id) + g_source_remove (priv->refresh_idle_id); + + g_free (priv); + + if (G_OBJECT_CLASS (parent_class)->finalize) + (* G_OBJECT_CLASS (parent_class)->finalize) (obj); +} + +GtkObject * +e_meeting_model_new (void) +{ + return g_object_new (E_TYPE_MEETING_MODEL, NULL); +} + + +CalClient * +e_meeting_model_get_cal_client (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + return priv->client; +} + +void +e_meeting_model_set_cal_client (EMeetingModel *im, CalClient *client) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + if (priv->client != NULL) + g_object_unref (priv->client); + + if (client != NULL) + g_object_ref (client); + priv->client = client; +} + +icaltimezone * +e_meeting_model_get_zone (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + + g_return_val_if_fail (im != NULL, NULL); + g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); + + priv = im->priv; + + return priv->zone; +} + +void +e_meeting_model_set_zone (EMeetingModel *im, icaltimezone *zone) +{ + EMeetingModelPrivate *priv; + + g_return_if_fail (im != NULL); + g_return_if_fail (E_IS_MEETING_MODEL (im)); + + priv = im->priv; + + priv->zone = zone; +} + +static ETableScrolled * +build_etable (ETableModel *model, const gchar *spec_file, const gchar *state_file) +{ + GtkWidget *etable; + ETable *real_table; + ETableExtras *extras; + GList *strings; + ECell *popup_cell, *cell; + + extras = e_table_extras_new (); + + /* For type */ + 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); + g_object_unref (cell); + + strings = NULL; + strings = g_list_append (strings, (char*) _("Individual")); + strings = g_list_append (strings, (char*) _("Group")); + strings = g_list_append (strings, (char*) _("Resource")); + strings = g_list_append (strings, (char*) _("Room")); + strings = g_list_append (strings, (char*) _("Unknown")); + + e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); + e_table_extras_add_cell (extras, "typeedit", popup_cell); + + /* 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); + g_object_unref (cell); + + strings = NULL; + strings = g_list_append (strings, (char*) _("Chair")); + strings = g_list_append (strings, (char*) _("Required Participant")); + strings = g_list_append (strings, (char*) _("Optional Participant")); + strings = g_list_append (strings, (char*) _("Non-Participant")); + strings = g_list_append (strings, (char*) _("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); + g_object_unref (cell); + + strings = NULL; + strings = g_list_append (strings, (char*) _("Yes")); + strings = g_list_append (strings, (char*) _("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); + g_object_unref (cell); + + strings = NULL; + strings = g_list_append (strings, (char*) _("Needs Action")); + strings = g_list_append (strings, (char*) _("Accepted")); + strings = g_list_append (strings, (char*) _("Declined")); + strings = g_list_append (strings, (char*) _("Tentative")); + strings = g_list_append (strings, (char*) _("Delegated")); + + e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); + e_table_extras_add_cell (extras, "statusedit", popup_cell); + + etable = e_table_scrolled_new_from_spec_file (model, extras, spec_file, NULL); + real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (etable)); + g_object_set (G_OBJECT (real_table), "uniform_row_height", TRUE, NULL); + e_table_load_state (real_table, state_file); + +#if 0 + g_signal_connect (real_table, "right_click", G_CALLBACK (right_click_cb), mpage); +#endif + + g_signal_connect (etable, "destroy", G_CALLBACK (table_destroy_state_cb), g_strdup (state_file)); + + g_object_unref (extras); + + return E_TABLE_SCROLLED (etable); +} + +void +e_meeting_model_add_attendee (EMeetingModel *im, EMeetingAttendee *ia) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + e_table_model_pre_change (E_TABLE_MODEL (im)); + + g_object_ref (ia); + g_ptr_array_add (priv->attendees, ia); + + g_signal_connect (ia, "changed", G_CALLBACK (attendee_changed_cb), im); + + e_table_model_row_inserted (E_TABLE_MODEL (im), row_count (E_TABLE_MODEL (im)) - 1); +} + +EMeetingAttendee * +e_meeting_model_add_attendee_with_defaults (EMeetingModel *im) +{ + EMeetingAttendee *ia; + char *str; + + ia = E_MEETING_ATTENDEE (e_meeting_attendee_new ()); + + e_meeting_attendee_set_address (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_ADDRESS_COL)); + e_meeting_attendee_set_member (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_MEMBER_COL)); + + str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_TYPE_COL); + e_meeting_attendee_set_cutype (ia, text_to_type (str)); + g_free (str); + str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_ROLE_COL); + e_meeting_attendee_set_role (ia, text_to_role (str)); + g_free (str); + str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_RSVP_COL); + e_meeting_attendee_set_rsvp (ia, text_to_boolean (str)); + g_free (str); + + e_meeting_attendee_set_delto (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_DELTO_COL)); + e_meeting_attendee_set_delfrom (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_DELFROM_COL)); + + str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_STATUS_COL); + e_meeting_attendee_set_status (ia, text_to_partstat (str)); + g_free (str); + + e_meeting_attendee_set_cn (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_CN_COL)); + e_meeting_attendee_set_language (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_LANGUAGE_COL)); + + e_meeting_model_add_attendee (im, ia); + + return ia; +} + +void +e_meeting_model_remove_attendee (EMeetingModel *im, EMeetingAttendee *ia) +{ + EMeetingModelPrivate *priv; + gint i, row = -1; + + priv = im->priv; + + for (i = 0; i < priv->attendees->len; i++) { + if (ia == g_ptr_array_index (priv->attendees, i)) { + row = i; + break; + } + } + + if (row != -1) { + e_table_model_pre_change (E_TABLE_MODEL (im)); + + g_ptr_array_remove_index (priv->attendees, row); + g_object_unref (ia); + + e_table_model_row_deleted (E_TABLE_MODEL (im), row); + } +} + +void +e_meeting_model_remove_all_attendees (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + gint i, len; + + priv = im->priv; + + e_table_model_pre_change (E_TABLE_MODEL (im)); + + len = priv->attendees->len; + + for (i = 0; i < len; i++) { + EMeetingAttendee *ia = g_ptr_array_index (priv->attendees, i); + g_object_unref (ia); + } + g_ptr_array_set_size (priv->attendees, 0); + + e_table_model_rows_deleted (E_TABLE_MODEL (im), 0, len); +} + +EMeetingAttendee * +e_meeting_model_find_attendee (EMeetingModel *im, const gchar *address, gint *row) +{ + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + int i; + + priv = im->priv; + + if (address == NULL) + return NULL; + + for (i = 0; i < priv->attendees->len; i++) { + const gchar *ia_address; + + ia = g_ptr_array_index (priv->attendees, i); + + ia_address = e_meeting_attendee_get_address (ia); + if (ia_address && !g_strcasecmp (itip_strip_mailto (ia_address), itip_strip_mailto (address))) { + if (row != NULL) + *row = i; + + return ia; + } + } + + return NULL; +} + +EMeetingAttendee * +e_meeting_model_find_attendee_at_row (EMeetingModel *im, gint row) +{ + EMeetingModelPrivate *priv; + + g_return_val_if_fail (im != NULL, NULL); + g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); + g_return_val_if_fail (row >= 0, NULL); + + priv = im->priv; + g_return_val_if_fail (row < priv->attendees->len, NULL); + + return g_ptr_array_index (priv->attendees, row); +} + +gint +e_meeting_model_count_actual_attendees (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + return e_table_model_row_count (E_TABLE_MODEL (im)); +} + +const GPtrArray * +e_meeting_model_get_attendees (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + return priv->attendees; +} + +static icaltimezone * +find_zone (icalproperty *ip, icalcomponent *tz_top_level) +{ + icalparameter *param; + icalcomponent *sub_comp; + const char *tzid; + icalcompiter iter; + + if (tz_top_level == NULL) + return NULL; + + param = icalproperty_get_first_parameter (ip, ICAL_TZID_PARAMETER); + if (param == NULL) + return NULL; + tzid = icalparameter_get_tzid (param); + + iter = icalcomponent_begin_component (tz_top_level, ICAL_VTIMEZONE_COMPONENT); + while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { + icalcomponent *clone; + const char *tz_tzid; + + /* FIXME We aren't passing a property here */ + tz_tzid = icalproperty_get_tzid (sub_comp); + if (!strcmp (tzid, tz_tzid)) { + icaltimezone *zone; + + zone = icaltimezone_new (); + clone = icalcomponent_new_clone (sub_comp); + icaltimezone_set_component (zone, clone); + + return zone; + } + + icalcompiter_next (&iter); + } + + return NULL; +} + + +static void +refresh_queue_add (EMeetingModel *im, int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingModelRefreshCallback call_back, + gpointer data) +{ + EMeetingModelPrivate *priv; + EMeetingAttendee *ia; + EMeetingModelQueueData *qdata; + + priv = im->priv; + + ia = g_ptr_array_index (priv->attendees, row); + if (ia == NULL) + return; + + qdata = g_hash_table_lookup (priv->refresh_data, ia); + if (qdata == NULL) { + qdata = g_new0 (EMeetingModelQueueData, 1); + + qdata->im = im; + qdata->ia = ia; + e_meeting_attendee_clear_busy_periods (ia); + e_meeting_attendee_set_has_calendar_info (ia, FALSE); + + qdata->start = *start; + qdata->end = *end; + qdata->string = g_string_new (NULL); + qdata->call_backs = g_ptr_array_new (); + qdata->data = g_ptr_array_new (); + g_ptr_array_add (qdata->call_backs, call_back); + g_ptr_array_add (qdata->data, data); + + g_hash_table_insert (priv->refresh_data, ia, qdata); + } else { + if (e_meeting_time_compare_times (start, &qdata->start) == -1) + qdata->start = *start; + if (e_meeting_time_compare_times (end, &qdata->end) == 1) + qdata->end = *end; + g_ptr_array_add (qdata->call_backs, call_back); + g_ptr_array_add (qdata->data, data); + } + + g_object_ref (ia); + g_ptr_array_add (priv->refresh_queue, ia); + + if (priv->refresh_idle_id == 0) + priv->refresh_idle_id = g_idle_add (refresh_busy_periods, im); +} + +static void +refresh_queue_remove (EMeetingModel *im, EMeetingAttendee *ia) +{ + EMeetingModelPrivate *priv; + EMeetingModelQueueData *qdata; + + priv = im->priv; + + /* Free the queue data */ + qdata = g_hash_table_lookup (priv->refresh_data, ia); + g_assert (qdata != NULL); + + g_hash_table_remove (priv->refresh_data, ia); + g_ptr_array_free (qdata->call_backs, TRUE); + g_ptr_array_free (qdata->data, TRUE); + g_free (qdata); + + /* Unref the attendee */ + g_ptr_array_remove (priv->refresh_queue, ia); + g_object_unref (ia); +} + +static void +process_callbacks (EMeetingModelQueueData *qdata) +{ + EMeetingModel *im; + int i; + + for (i = 0; i < qdata->call_backs->len; i++) { + EMeetingModelRefreshCallback call_back; + gpointer *data; + + call_back = g_ptr_array_index (qdata->call_backs, i); + data = g_ptr_array_index (qdata->data, i); + + call_back (data); + } + + im = qdata->im; + refresh_queue_remove (qdata->im, qdata->ia); + g_object_unref (im); +} + +static void +process_free_busy_comp (EMeetingAttendee *ia, + icalcomponent *fb_comp, + icaltimezone *zone, + icalcomponent *tz_top_level) +{ + icalproperty *ip; + + ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY); + if (ip != NULL) { + struct icaltimetype dtstart; + icaltimezone *ds_zone; + + dtstart = icalproperty_get_dtstart (ip); + if (!dtstart.is_utc) + ds_zone = find_zone (ip, tz_top_level); + else + ds_zone = icaltimezone_get_utc_timezone (); + icaltimezone_convert_time (&dtstart, ds_zone, zone); + e_meeting_attendee_set_start_busy_range (ia, + dtstart.year, + dtstart.month, + dtstart.day, + dtstart.hour, + dtstart.minute); + } + + ip = icalcomponent_get_first_property (fb_comp, ICAL_DTEND_PROPERTY); + if (ip != NULL) { + struct icaltimetype dtend; + icaltimezone *de_zone; + + dtend = icalproperty_get_dtend (ip); + if (!dtend.is_utc) + de_zone = find_zone (ip, tz_top_level); + else + de_zone = icaltimezone_get_utc_timezone (); + icaltimezone_convert_time (&dtend, de_zone, zone); + e_meeting_attendee_set_end_busy_range (ia, + dtend.year, + dtend.month, + dtend.day, + dtend.hour, + dtend.minute); + } + + ip = icalcomponent_get_first_property (fb_comp, ICAL_FREEBUSY_PROPERTY); + while (ip != NULL) { + icalparameter *param; + struct icalperiodtype fb; + EMeetingFreeBusyType busy_type = E_MEETING_FREE_BUSY_LAST; + icalparameter_fbtype fbtype = ICAL_FBTYPE_BUSY; + + fb = icalproperty_get_freebusy (ip); + param = icalproperty_get_first_parameter (ip, ICAL_FBTYPE_PARAMETER); + if (param != NULL) + fbtype = icalparameter_get_fbtype (param); + + switch (fbtype) { + case ICAL_FBTYPE_BUSY: + busy_type = E_MEETING_FREE_BUSY_BUSY; + break; + + case ICAL_FBTYPE_BUSYUNAVAILABLE: + busy_type = E_MEETING_FREE_BUSY_OUT_OF_OFFICE; + break; + + case ICAL_FBTYPE_BUSYTENTATIVE: + busy_type = E_MEETING_FREE_BUSY_TENTATIVE; + break; + + default: + break; + } + + if (busy_type != E_MEETING_FREE_BUSY_LAST) { + icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); + + icaltimezone_convert_time (&fb.start, utc_zone, zone); + icaltimezone_convert_time (&fb.end, utc_zone, zone); + e_meeting_attendee_add_busy_period (ia, + fb.start.year, + fb.start.month, + fb.start.day, + fb.start.hour, + fb.start.minute, + fb.end.year, + fb.end.month, + fb.end.day, + fb.end.hour, + fb.end.minute, + busy_type); + } + + ip = icalcomponent_get_next_property (fb_comp, ICAL_FREEBUSY_PROPERTY); + } +} + +static void +process_free_busy (EMeetingModelQueueData *qdata, char *text) +{ + EMeetingModel *im = qdata->im; + EMeetingModelPrivate *priv; + EMeetingAttendee *ia = qdata->ia; + icalcomponent *main_comp; + icalcomponent_kind kind = ICAL_NO_COMPONENT; + + priv = im->priv; + + main_comp = icalparser_parse_string (text); + if (main_comp == NULL) { + process_callbacks (qdata); + return; + } + + kind = icalcomponent_isa (main_comp); + if (kind == ICAL_VCALENDAR_COMPONENT) { + icalcompiter iter; + icalcomponent *tz_top_level, *sub_comp; + + tz_top_level = cal_util_new_top_level (); + + iter = icalcomponent_begin_component (main_comp, ICAL_VTIMEZONE_COMPONENT); + while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { + icalcomponent *clone; + + clone = icalcomponent_new_clone (sub_comp); + icalcomponent_add_component (tz_top_level, clone); + + icalcompiter_next (&iter); + } + + iter = icalcomponent_begin_component (main_comp, ICAL_VFREEBUSY_COMPONENT); + while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { + process_free_busy_comp (ia, sub_comp, priv->zone, tz_top_level); + + icalcompiter_next (&iter); + } + icalcomponent_free (tz_top_level); + } else if (kind == ICAL_VFREEBUSY_COMPONENT) { + process_free_busy_comp (ia, main_comp, priv->zone, NULL); + } + + icalcomponent_free (main_comp); + + process_callbacks (qdata); +} + +static void +async_close (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer data) +{ + EMeetingModelQueueData *qdata = data; + + process_free_busy (qdata, qdata->string->str); +} + +static void +async_read (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer buffer, + GnomeVFSFileSize requested, + GnomeVFSFileSize read, + gpointer data) +{ + EMeetingModelQueueData *qdata = data; + GnomeVFSFileSize buf_size = BUF_SIZE - 1; + + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) { + gnome_vfs_async_close (handle, async_close, qdata); + return; + } + + ((char *)buffer)[read] = '\0'; + qdata->string = g_string_append (qdata->string, buffer); + + if (result == GNOME_VFS_ERROR_EOF) { + gnome_vfs_async_close (handle, async_close, qdata); + return; + } + + gnome_vfs_async_read (handle, qdata->buffer, buf_size, async_read, qdata); +} + +static void +async_open (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer data) +{ + EMeetingModelQueueData *qdata = data; + GnomeVFSFileSize buf_size = BUF_SIZE - 1; + + if (result != GNOME_VFS_OK) { + gnome_vfs_async_close (handle, async_close, qdata); + return; + } + + gnome_vfs_async_read (handle, qdata->buffer, buf_size, async_read, qdata); +} + +static void +contacts_cb (EBook *book, EBookStatus status, GList *contacts, gpointer data) +{ + EMeetingModelQueueData *qdata = data; + GList *l; + + if (status != E_BOOK_ERROR_OK) + return; + + for (l = contacts; l; l = l->next) { + GnomeVFSAsyncHandle *handle; + EContact *contact = E_CONTACT (l->data); + const char *fburl = e_contact_get_const (contact, E_CONTACT_FREEBUSY_URL); + + if (!fburl || !*fburl) + continue; + + /* Read in free/busy data from the url */ + gnome_vfs_async_open (&handle, fburl, GNOME_VFS_OPEN_READ, + GNOME_VFS_PRIORITY_DEFAULT, async_open, qdata); + + e_free_object_list (contacts); + return; + } + + process_callbacks (qdata); + e_free_object_list (contacts); +} + +static gboolean +refresh_busy_periods (gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + EMeetingAttendee *ia = NULL; + EMeetingModelQueueData *qdata = NULL; + char *query; + int i; + + priv = im->priv; + + /* Check to see if there are any remaining attendees in the queue */ + for (i = 0; i < priv->refresh_queue->len; i++) { + ia = g_ptr_array_index (priv->refresh_queue, i); + g_assert (ia != NULL); + + qdata = g_hash_table_lookup (priv->refresh_data, ia); + g_assert (qdata != NULL); + + if (!qdata->refreshing) + break; + } + + /* The everything in the queue is being refreshed */ + if (i >= priv->refresh_queue->len) { + priv->refresh_idle_id = 0; + return FALSE; + } + + /* Indicate we are trying to refresh it */ + qdata->refreshing = TRUE; + + /* We take a ref in case we get destroyed in the gui during a callback */ + g_object_ref (qdata->im); + + /* Check the server for free busy data */ + if (priv->client) { + GList *fb_data = NULL, *users = NULL; + struct icaltimetype itt; + time_t startt, endt; + const char *user; + + itt = icaltime_null_time (); + itt.year = g_date_year (&qdata->start.date); + itt.month = g_date_month (&qdata->start.date); + itt.day = g_date_day (&qdata->start.date); + itt.hour = qdata->start.hour; + itt.minute = qdata->start.minute; + startt = icaltime_as_timet_with_zone (itt, priv->zone); + + itt = icaltime_null_time (); + itt.year = g_date_year (&qdata->end.date); + itt.month = g_date_month (&qdata->end.date); + itt.day = g_date_day (&qdata->end.date); + itt.hour = qdata->end.hour; + itt.minute = qdata->end.minute; + endt = icaltime_as_timet_with_zone (itt, priv->zone); + + user = itip_strip_mailto (e_meeting_attendee_get_address (ia)); + users = g_list_append (users, g_strdup (user)); + + /* FIXME Error checking */ + cal_client_get_free_busy (priv->client, users, startt, endt, &fb_data, NULL); + + g_list_foreach (users, (GFunc)g_free, NULL); + g_list_free (users); + + if (fb_data != NULL) { + CalComponent *comp = fb_data->data; + char *comp_str; + + comp_str = cal_component_get_as_string (comp); + process_free_busy (qdata, comp_str); + g_free (comp_str); + return TRUE; + } + } + + /* Look for fburl's of attendee with no free busy info on server */ + if (!priv->book_loaded) { + priv->book_load_wait = TRUE; + gtk_main (); + } + + if (!e_meeting_attendee_is_set_address (ia)) { + process_callbacks (qdata); + return TRUE; + } + + query = g_strdup_printf ("(is \"email\" \"%s\")", + itip_strip_mailto (e_meeting_attendee_get_address (ia))); + if (!e_book_async_get_contacts (priv->ebook, query, contacts_cb, qdata)) + process_callbacks (qdata); + g_free (query); + + return TRUE; +} + +void +e_meeting_model_refresh_all_busy_periods (EMeetingModel *im, + EMeetingTime *start, + EMeetingTime *end, + EMeetingModelRefreshCallback call_back, + gpointer data) +{ + EMeetingModelPrivate *priv; + int i; + + g_return_if_fail (im != NULL); + g_return_if_fail (E_IS_MEETING_MODEL (im)); + + priv = im->priv; + + for (i = 0; i < priv->attendees->len; i++) + refresh_queue_add (im, i, start, end, call_back, data); +} + +void +e_meeting_model_refresh_busy_periods (EMeetingModel *im, + int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingModelRefreshCallback call_back, + gpointer data) +{ + EMeetingModelPrivate *priv; + + g_return_if_fail (im != NULL); + g_return_if_fail (E_IS_MEETING_MODEL (im)); + + priv = im->priv; + + refresh_queue_add (im, row, start, end, call_back, data); +} + +ETableScrolled * +e_meeting_model_etable_from_model (EMeetingModel *im, const gchar *spec_file, const gchar *state_file) +{ + EMeetingModelPrivate *priv; + ETableScrolled *ets; + + g_return_val_if_fail (im != NULL, NULL); + g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); + + priv = im->priv; + + ets = build_etable (E_TABLE_MODEL (im), spec_file, state_file); + + priv->tables = g_list_prepend (priv->tables, ets); + + g_signal_connect (ets, "destroy", G_CALLBACK (table_destroy_list_cb), im); + + return ets; +} + +void +e_meeting_model_etable_click_to_add (EMeetingModel *im, gboolean click_to_add) +{ + EMeetingModelPrivate *priv; + GList *l; + + g_return_if_fail (im != NULL); + g_return_if_fail (E_IS_MEETING_MODEL (im)); + + priv = im->priv; + + for (l = priv->tables; l != NULL; l = l->next) { + ETableScrolled *ets; + ETable *real_table; + + ets = l->data; + real_table = e_table_scrolled_get_table (ets); + + g_object_set (G_OBJECT (real_table), "use_click_to_add", click_to_add, NULL); + } +} + +int +e_meeting_model_etable_model_to_view_row (ETable *et, EMeetingModel *im, int model_row) +{ + EMeetingModelPrivate *priv; + + g_return_val_if_fail (im != NULL, -1); + g_return_val_if_fail (E_IS_MEETING_MODEL (im), -1); + + priv = im->priv; + + return e_table_model_to_view_row (et, model_row); +} + +int +e_meeting_model_etable_view_to_model_row (ETable *et, EMeetingModel *im, int view_row) +{ + EMeetingModelPrivate *priv; + + g_return_val_if_fail (im != NULL, -1); + g_return_val_if_fail (E_IS_MEETING_MODEL (im), -1); + + priv = im->priv; + + return e_table_view_to_model_row (et, view_row); +} + + +static void +add_section (GNOME_Evolution_Addressbook_SelectNames corba_select_names, const char *name) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Addressbook_SelectNames_addSection (corba_select_names, + name, + gettext (name), + &ev); + + CORBA_exception_free (&ev); +} + +static gboolean +get_select_name_dialog (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + CORBA_Environment ev; + int i; + + priv = im->priv; + + if (priv->corba_select_names != CORBA_OBJECT_NIL) { + Bonobo_Control corba_control; + GtkWidget *control_widget; + int i; + + CORBA_exception_init (&ev); + for (i = 0; sections[i] != NULL; i++) { + corba_control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection + (priv->corba_select_names, sections[i], &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return FALSE; + } + + control_widget = bonobo_widget_new_control_from_objref (corba_control, CORBA_OBJECT_NIL); + + bonobo_widget_set_property (BONOBO_WIDGET (control_widget), "text", TC_CORBA_string, "", NULL); + } + CORBA_exception_free (&ev); + + return TRUE; + } + + CORBA_exception_init (&ev); + + priv->corba_select_names = bonobo_activation_activate_from_id (SELECT_NAMES_OAFID, 0, NULL, &ev); + + for (i = 0; sections[i] != NULL; i++) + add_section (priv->corba_select_names, sections[i]); + + bonobo_event_source_client_add_listener (priv->corba_select_names, + (BonoboListenerCallbackFn) select_names_ok_cb, + "GNOME/Evolution:ok:dialog", + NULL, im); + + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return FALSE; + } + + CORBA_exception_free (&ev); + + return TRUE; +} + +void +e_meeting_model_invite_others_dialog (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + CORBA_Environment ev; + + priv = im->priv; + + if (!get_select_name_dialog (im)) + return; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Addressbook_SelectNames_activateDialog ( + priv->corba_select_names, _("Required Participants"), &ev); + + CORBA_exception_free (&ev); +} + +static void +process_section (EMeetingModel *im, EABDestination **destv, icalparameter_role role) +{ + EMeetingModelPrivate *priv; + int i; + + if (!destv) + return; + + priv = im->priv; + for (i = 0; destv[i]; i++) { + EMeetingAttendee *ia; + const char *name, *attendee = NULL; + char *attr; + CORBA_Environment ev; + EABDestination *dest = destv[i]; + + CORBA_exception_init (&ev); + + /* Get the CN */ + name = eab_destination_get_name (dest); + + /* Get the field as attendee from the backend */ + if (cal_client_get_ldap_attribute (priv->client, &attr, NULL) && attr) { + /* FIXME this should be more general */ + if (!strcmp (attr, "icscalendar")) { + EContact *contact = eab_destination_get_contact (dest); + if (contact) + attendee = e_contact_get_const (contact, E_CONTACT_ICS_CALENDAR); + } + + g_free (attr); + } + + CORBA_exception_init (&ev); + + /* If we couldn't get the attendee prior, get the email address as the default */ + if (attendee == NULL || *attendee == '\0') { + attendee = eab_destination_get_email (dest); + } + + CORBA_exception_free (&ev); + + if (attendee == NULL || *attendee == '\0') + continue; + + if (e_meeting_model_find_attendee (im, attendee, NULL) == NULL) { + ia = e_meeting_model_add_attendee_with_defaults (im); + + e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", attendee)); + e_meeting_attendee_set_role (ia, role); + if (role == ICAL_ROLE_NONPARTICIPANT) + e_meeting_attendee_set_cutype (ia, ICAL_CUTYPE_RESOURCE); + e_meeting_attendee_set_cn (ia, g_strdup (name)); + } + } +} + +static void +select_names_ok_cb (BonoboListener *listener, + const char *event_name, + const CORBA_any *arg, + CORBA_Environment *ev, + gpointer data) +{ + EMeetingModel *im = data; + EMeetingModelPrivate *priv; + Bonobo_Control corba_control; + GtkWidget *control_widget; + BonoboControlFrame *control_frame; + Bonobo_PropertyBag pb; + char *dest_str; + EABDestination **dest; + int i; + + priv = im->priv; + + for (i = 0; sections[i] != NULL; i++) { + corba_control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection + (priv->corba_select_names, sections[i], ev); + control_widget = bonobo_widget_new_control_from_objref + (corba_control, CORBA_OBJECT_NIL); + + control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (control_widget)); + pb = bonobo_control_frame_get_control_property_bag (control_frame, NULL); + dest_str = bonobo_pbclient_get_string (pb, "destinations", NULL); + dest = eab_destination_importv (dest_str); + process_section (im, dest, roles[i]); + eab_destination_freev (dest); + } +} + +static void +attendee_changed_cb (EMeetingAttendee *ia, gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + gint row = -1, i; + + priv = im->priv; + + /* FIXME: Ideally I think you are supposed to call pre_change() before + the data structures are changed. */ + e_table_model_pre_change (E_TABLE_MODEL (im)); + + for (i = 0; i < priv->attendees->len; i++) { + if (ia == g_ptr_array_index (priv->attendees, i)) { + row = i; + break; + } + } + + if (row == -1) + e_table_model_no_change (E_TABLE_MODEL (im)); + else + e_table_model_row_changed (E_TABLE_MODEL (im), row); +} + +static void +table_destroy_state_cb (ETableScrolled *etable, gpointer data) +{ + ETable *real_table; + char *filename = data; + + real_table = e_table_scrolled_get_table (etable); + e_table_save_state (real_table, filename); + + g_free (data); +} + +static void +table_destroy_list_cb (ETableScrolled *etable, gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + + priv = im->priv; + + priv->tables = g_list_remove (priv->tables, etable); +} + |