diff options
Diffstat (limited to 'calendar/gui/e-meeting-model.c')
-rw-r--r-- | calendar/gui/e-meeting-model.c | 1321 |
1 files changed, 1321 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..f492c89a07 --- /dev/null +++ b/calendar/gui/e-meeting-model.c @@ -0,0 +1,1321 @@ +/* -*- Mode: 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 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. + * + * Author: JP Rosevear + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomevfs/gnome-vfs.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 <e-book.h> +#include <e-card-types.h> +#include <e-card-cursor.h> +#include <e-card.h> +#include <e-card-simple.h> +#include <cal-util/cal-component.h> +#include <cal-util/cal-util.h> +#include <cal-util/timeutil.h> +#include "calendar-config.h" +#include "itip-utils.h" +#include "e-meeting-attendee.h" +#include "e-meeting-model.h" + +enum columns { + ITIP_ADDRESS_COL, + ITIP_MEMBER_COL, + ITIP_TYPE_COL, + ITIP_ROLE_COL, + ITIP_RSVP_COL, + ITIP_DELTO_COL, + ITIP_DELFROM_COL, + ITIP_STATUS_COL, + ITIP_CN_COL, + ITIP_LANGUAGE_COL, + ITIP_COLUMN_COUNT +}; + +struct _EMeetingModelPrivate +{ + GPtrArray *attendees; + + CalClient *client; + + EBook *ebook; + gboolean book_loaded; + gboolean book_load_wait; + + GList *refresh_callbacks; + GList *refresh_data; + gint refresh_count; + gboolean refreshing; +}; + +#define BUF_SIZE 1024 + +typedef struct _EMeetingModelAttendeeRefreshData EMeetingModelAttendeeRefreshData; +struct _EMeetingModelAttendeeRefreshData { + char buffer[BUF_SIZE]; + GString *string; + + EMeetingAttendee *ia; +}; + +typedef struct _EMeetingModelRefreshData EMeetingModelRefreshData; +struct _EMeetingModelRefreshData { + EMeetingModel *im; + + EMeetingModelAttendeeRefreshData attendee_data; +}; + + +static void class_init (EMeetingModelClass *klass); +static void init (EMeetingModel *model); +static void destroy (GtkObject *obj); + +static void attendee_changed_cb (EMeetingAttendee *ia, gpointer data); + +static ETableModelClass *parent_class = NULL; + + +GtkType +e_meeting_model_get_type (void) +{ + static GtkType type = 0; + + if (type == 0) + { + static const GtkTypeInfo info = + { + "EMeetingModel", + sizeof (EMeetingModel), + sizeof (EMeetingModelClass), + (GtkClassInitFunc) class_init, + (GtkObjectInitFunc) init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + type = gtk_type_unique (e_table_model_get_type (), &info); + } + + return type; +} + +static void +book_open_cb (EBook *book, EBookStatus status, gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + + priv = im->priv; + + if (status == E_BOOK_STATUS_SUCCESS) + priv->book_loaded = TRUE; + else + g_warning ("Book not loaded"); + + if (priv->book_load_wait) { + priv->book_load_wait = FALSE; + gtk_main_quit (); + } +} + +static int +start_addressbook_server (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + gchar *uri, *path; + + priv = im->priv; + + priv->ebook = e_book_new (); + + path = g_concat_dir_and_file (g_get_home_dir (), + "evolution/local/Contacts/addressbook.db"); + uri = g_strdup_printf ("file://%s", path); + g_free (path); + + e_book_load_uri (priv->ebook, uri, book_open_cb, im); + + g_free (uri); +} + +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 ITIP_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, ITIP_ADDRESS_COL, row); + if (find_match (im, address, NULL) != NULL) { +// duplicate_error (); + 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, ITIP_MEMBER_COL, row))); + e_meeting_attendee_set_cutype (ia, text_to_type (e_table_model_value_at (source, ITIP_TYPE_COL, row))); + e_meeting_attendee_set_role (ia, text_to_role (e_table_model_value_at (source, ITIP_ROLE_COL, row))); + e_meeting_attendee_set_rsvp (ia, text_to_boolean (e_table_model_value_at (source, ITIP_RSVP_COL, row))); + e_meeting_attendee_set_delto (ia, g_strdup (e_table_model_value_at (source, ITIP_DELTO_COL, row))); + e_meeting_attendee_set_delfrom (ia, g_strdup (e_table_model_value_at (source, ITIP_DELFROM_COL, row))); + e_meeting_attendee_set_status (ia, text_to_partstat (e_table_model_value_at (source, ITIP_STATUS_COL, row))); + e_meeting_attendee_set_cn (ia, g_strdup (e_table_model_value_at (source, ITIP_CN_COL, row))); + e_meeting_attendee_set_language (ia, g_strdup (e_table_model_value_at (source, ITIP_LANGUAGE_COL, row))); + + e_meeting_model_add_attendee (E_MEETING_MODEL (etm), ia); + +// comp_editor_page_notify_needs_send (COMP_EDITOR_PAGE (im)); +// comp_editor_page_notify_changed (COMP_EDITOR_PAGE (im)); +} + +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 ITIP_ADDRESS_COL: + return (void *)itip_strip_mailto (e_meeting_attendee_get_address (ia)); + case ITIP_MEMBER_COL: + return (void *)e_meeting_attendee_get_member (ia); + case ITIP_TYPE_COL: + return type_to_text (e_meeting_attendee_get_cutype (ia)); + case ITIP_ROLE_COL: + return role_to_text (e_meeting_attendee_get_role (ia)); + case ITIP_RSVP_COL: + return boolean_to_text (e_meeting_attendee_get_rsvp (ia)); + case ITIP_DELTO_COL: + return (void *)itip_strip_mailto (e_meeting_attendee_get_delto (ia)); + case ITIP_DELFROM_COL: + return (void *)itip_strip_mailto (e_meeting_attendee_get_delfrom (ia)); + case ITIP_STATUS_COL: + return partstat_to_text (e_meeting_attendee_get_status (ia)); + case ITIP_CN_COL: + return (void *)e_meeting_attendee_get_cn (ia); + case ITIP_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; + + im = E_MEETING_MODEL (etm); + priv = im->priv; + + ia = g_ptr_array_index (priv->attendees, row); + + switch (col) { + case ITIP_ADDRESS_COL: + e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", (char *) val)); + break; + case ITIP_MEMBER_COL: + e_meeting_attendee_set_member (ia, g_strdup (val)); + break; + case ITIP_TYPE_COL: + e_meeting_attendee_set_cutype (ia, text_to_type (val)); + break; + case ITIP_ROLE_COL: + e_meeting_attendee_set_role (ia, text_to_role (val)); + break; + case ITIP_RSVP_COL: + e_meeting_attendee_set_rsvp (ia, text_to_boolean (val)); + break; + case ITIP_DELTO_COL: + e_meeting_attendee_set_delto (ia, g_strdup (val)); + break; + case ITIP_DELFROM_COL: + e_meeting_attendee_set_delfrom (ia, g_strdup (val)); + break; + case ITIP_STATUS_COL: + e_meeting_attendee_set_status (ia, text_to_partstat (val)); + break; + case ITIP_CN_COL: + e_meeting_attendee_set_cn (ia, g_strdup (val)); + break; + case ITIP_LANGUAGE_COL: + e_meeting_attendee_set_language (ia, g_strdup (val)); + break; + } + +// comp_editor_page_notify_needs_send (COMP_EDITOR_PAGE (im)); +// comp_editor_page_notify_changed (COMP_EDITOR_PAGE (im)); +} + +static gboolean +is_cell_editable (ETableModel *etm, int col, int row) +{ + switch (col) { + case ITIP_DELTO_COL: + case ITIP_DELFROM_COL: + return FALSE; + + default: + } + + 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 ITIP_ADDRESS_COL: + return g_strdup (""); + case ITIP_MEMBER_COL: + return g_strdup (""); + case ITIP_TYPE_COL: + return g_strdup (_("Individual")); + case ITIP_ROLE_COL: + return g_strdup (_("Required Participant")); + case ITIP_RSVP_COL: + return g_strdup (_("Yes")); + case ITIP_DELTO_COL: + return g_strdup (""); + case ITIP_DELFROM_COL: + return g_strdup (""); + case ITIP_STATUS_COL: + return g_strdup (_("Needs Action")); + case ITIP_CN_COL: + return g_strdup (""); + case ITIP_LANGUAGE_COL: + return g_strdup ("en"); + } + + return g_strdup (""); +} + +static gboolean +value_is_empty (ETableModel *etm, int col, const void *val) +{ + + switch (col) { + case ITIP_ADDRESS_COL: + case ITIP_MEMBER_COL: + case ITIP_DELTO_COL: + case ITIP_DELFROM_COL: + case ITIP_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) +{ + GtkObjectClass *object_class; + ETableModelClass *etm_class; + + object_class = GTK_OBJECT_CLASS (klass); + etm_class = E_TABLE_MODEL_CLASS (klass); + + parent_class = gtk_type_class (E_TABLE_MODEL_TYPE); + + object_class->destroy = destroy; + + 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->client = NULL; + + priv->ebook = NULL; + priv->book_loaded = FALSE; + priv->book_load_wait = FALSE; + + priv->refreshing = FALSE; + + start_addressbook_server (im); +} + +static void +destroy (GtkObject *obj) +{ + EMeetingModel *model = E_MEETING_MODEL (obj); + EMeetingModelPrivate *priv; + gint i; + + priv = model->priv; + + for (i = 0; i < priv->attendees->len; i++) + gtk_object_unref (GTK_OBJECT (g_ptr_array_index(priv->attendees, i))); + g_ptr_array_free (priv->attendees, FALSE); + + if (priv->client != NULL) + gtk_object_unref (GTK_OBJECT (priv->client)); + + if (priv->ebook != NULL) + gtk_object_unref (GTK_OBJECT (priv->ebook)); + + g_free (priv); +} + +GtkObject * +e_meeting_model_new (void) +{ + return gtk_type_new (E_TYPE_MEETING_MODEL); +} + + +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) + gtk_object_unref (GTK_OBJECT (priv->client)); + + if (client != NULL) + gtk_object_ref (GTK_OBJECT (client)); + priv->client = client; +} + +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); + gtk_object_unref (GTK_OBJECT (cell)); + + strings = NULL; + strings = g_list_append (strings, _("Individual")); + strings = g_list_append (strings, _("Group")); + strings = g_list_append (strings, _("Resource")); + strings = g_list_append (strings, _("Room")); + strings = g_list_append (strings, _("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); + 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); + + + etable = e_table_scrolled_new_from_spec_file (model, extras, spec_file, NULL); + + real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (etable)); + e_scroll_frame_set_policy (E_SCROLL_FRAME (etable), GTK_POLICY_NEVER, GTK_POLICY_NEVER); + e_scroll_frame_set_scrollbar_spacing (E_SCROLL_FRAME (etable), 0); + e_table_load_state (real_table, state_file); + +// gtk_signal_connect (GTK_OBJECT (real_table), +// "right_click", GTK_SIGNAL_FUNC (right_click_cb), mpage); +// gtk_signal_connect (GTK_OBJECT (real_table->sort_info), +// "sort_info_changed", GTK_SIGNAL_FUNC (sort_info_changed_cb), mts); + + gtk_object_unref (GTK_OBJECT (extras)); + + return E_TABLE_SCROLLED (etable); +} + +void +e_meeting_model_add_attendee (EMeetingModel *im, EMeetingAttendee *ia) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + gtk_object_ref (GTK_OBJECT (ia)); + g_ptr_array_add (priv->attendees, ia); + + gtk_signal_connect (GTK_OBJECT (ia), "changed", + GTK_SIGNAL_FUNC (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), ITIP_ADDRESS_COL)); + e_meeting_attendee_set_member (ia, init_value (E_TABLE_MODEL (im), ITIP_MEMBER_COL)); + + str = init_value (E_TABLE_MODEL (im), ITIP_TYPE_COL); + e_meeting_attendee_set_cutype (ia, text_to_type (str)); + g_free (str); + str = init_value (E_TABLE_MODEL (im), ITIP_ROLE_COL); + e_meeting_attendee_set_role (ia, text_to_role (str)); + g_free (str); + str = init_value (E_TABLE_MODEL (im), ITIP_RSVP_COL); + e_meeting_attendee_set_role (ia, text_to_boolean (str)); + g_free (str); + + e_meeting_attendee_set_delto (ia, init_value (E_TABLE_MODEL (im), ITIP_DELTO_COL)); + e_meeting_attendee_set_delfrom (ia, init_value (E_TABLE_MODEL (im), ITIP_DELFROM_COL)); + + str = init_value (E_TABLE_MODEL (im), ITIP_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), ITIP_CN_COL)); + e_meeting_attendee_set_language (ia, init_value (E_TABLE_MODEL (im), ITIP_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) { + g_ptr_array_remove_index (priv->attendees, row); + gtk_object_unref (GTK_OBJECT (ia)); + + e_table_model_row_deleted (E_TABLE_MODEL (im), row); + } +} + +void +e_meeting_model_remove_all_attendees (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + gint i; + + priv = im->priv; + + for (i = 0; i < priv->attendees->len; i++) { + EMeetingAttendee *ia = g_ptr_array_index (priv->attendees, i); + gtk_object_unref (GTK_OBJECT (ia)); + } + + e_table_model_rows_deleted (E_TABLE_MODEL (im), 0, priv->attendees->len); + g_ptr_array_set_size (priv->attendees, 0); +} + +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; + + priv = im->priv; + + return g_ptr_array_index (priv->attendees, row); +} + +gint +e_meeting_model_count_attendees (EMeetingModel *im) +{ + EMeetingModelPrivate *priv; + + priv = im->priv; + + return priv->attendees->len; +} + +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); + 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; + + 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 struct icaltimetype +convert_time (struct icaltimetype itt, icaltimezone *from, icaltimezone *to) +{ + if (from == NULL) + from = icaltimezone_get_utc_timezone (); + + icaltimezone_convert_time (&itt, from, to); + + return itt; +} + +static void +process_free_busy_comp (EMeetingAttendee *ia, icalcomponent *fb_comp, icalcomponent *tz_top_level) +{ + icalproperty *ip; + icaltimezone *view_zone; + + view_zone = icaltimezone_get_builtin_timezone (calendar_config_get_timezone ()); + + ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY); + if (ip != NULL) { + struct icaltimetype dtstart; + icaltimezone *ds_zone = NULL; + + dtstart = icalproperty_get_dtstart (ip); + if (!dtstart.is_utc) { + ds_zone = find_zone (ip, tz_top_level); + if (ds_zone != NULL) + dtstart = convert_time (dtstart, ds_zone, view_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 = NULL; + + dtend = icalproperty_get_dtend (ip); + if (!dtend.is_utc) { + de_zone = find_zone (ip, tz_top_level); + if (de_zone != NULL) + dtend = convert_time (dtend, de_zone, view_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: + } + + if (fbtype != E_MEETING_FREE_BUSY_LAST) { + fb.start = convert_time (fb.start, NULL, view_zone); + fb.end = convert_time (fb.end, NULL, view_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 (EMeetingModel *im, EMeetingAttendee *ia, char *text) +{ + EMeetingModelPrivate *priv; + icalcomponent *main_comp; + icalcomponent_kind kind = ICAL_NO_COMPONENT; + + priv = im->priv; + + main_comp = icalparser_parse_string (text); + if (main_comp == NULL) + 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, tz_top_level); + + icalcompiter_next (&iter); + } + icalcomponent_free (tz_top_level); + } else if (kind == ICAL_VFREEBUSY_COMPONENT) { + process_free_busy_comp (ia, main_comp, NULL); + } else { + return; + } + + icalcomponent_free (main_comp); +} + +static void +async_close (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer data) +{ + EMeetingModelRefreshData *r_data = data; + EMeetingModelPrivate *priv; + + process_free_busy (r_data->im, r_data->attendee_data.ia, r_data->attendee_data.string->str); + + priv = r_data->im->priv; + + priv->refresh_count--; + + if (priv->refresh_count == 0) { + GList *l, *m; + + for (l = priv->refresh_callbacks, m = priv->refresh_data; l != NULL; l = l->next, m = m->next) { + EMeetingModelRefreshCallback cb = l->data; + + cb (m->data); + } + + priv->refreshing = FALSE; + } +} + +static void +async_read (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer buffer, + GnomeVFSFileSize requested, + GnomeVFSFileSize read, + gpointer data) +{ + EMeetingModelRefreshData *r_data = data; + GnomeVFSFileSize buf_size = BUF_SIZE - 1; + + if (result != GNOME_VFS_OK) { + gnome_vfs_async_close (handle, async_close, r_data); + return; + } + + ((char *)buffer)[read] = '\0'; + r_data->attendee_data.string = g_string_append (r_data->attendee_data.string, buffer); + + if (read < requested) { + gnome_vfs_async_close (handle, async_close, r_data); + return; + } + + gnome_vfs_async_read (handle, r_data->attendee_data.buffer, buf_size, async_read, r_data); +} + +static void +async_open (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer data) +{ + EMeetingModelRefreshData *r_data = data; + GnomeVFSFileSize buf_size = BUF_SIZE - 1; + + gnome_vfs_async_read (handle, r_data->attendee_data.buffer, buf_size, async_read, r_data); +} + +static void +cursor_cb (EBook *book, EBookStatus status, ECardCursor *cursor, gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + int length, i, j; + + if (status != E_BOOK_STATUS_SUCCESS) + return; + + priv = im->priv; + + length = e_card_cursor_get_length (cursor); + priv->refresh_count = 0; + + for (i = 0; i < length; i ++) { + GnomeVFSAsyncHandle *handle; + ECard *card = e_card_cursor_get_nth (cursor, i); + EMeetingModelRefreshData *r_data = g_new0 (EMeetingModelRefreshData, 1); + EMeetingAttendee *ia = NULL; + + if (card->fburl == NULL) + continue; + + for (j = 0; j < priv->attendees->len; j++) { + ia = g_ptr_array_index (priv->attendees, j); + if (e_card_email_match_string (card, itip_strip_mailto (e_meeting_attendee_get_address (ia)))) + break; + } + if (ia == NULL) + continue; + + r_data->im = im; + r_data->attendee_data.string = g_string_new (NULL); + r_data->attendee_data.ia = ia; + + priv->refresh_count++; + + /* Read in free/busy data from the url */ + gnome_vfs_async_open (&handle, card->fburl, GNOME_VFS_OPEN_READ, async_open, r_data); + } +} + +void +e_meeting_model_refresh_busy_periods (EMeetingModel *im, EMeetingModelRefreshCallback call_back, gpointer data) +{ + EMeetingModelPrivate *priv; + GPtrArray *not_found; + GString *string; + int i; + + priv = im->priv; + + priv->refresh_callbacks = g_list_append (priv->refresh_callbacks, call_back); + priv->refresh_data = g_list_append (priv->refresh_data, data); + + if (priv->refreshing) + return; + + priv->refreshing = TRUE; + + /* To track what we don't find on the server */ + not_found = g_ptr_array_new (); + g_ptr_array_set_size (not_found, priv->attendees->len); + for (i = 0; i < priv->attendees->len; i++) + g_ptr_array_index (not_found, i) = g_ptr_array_index (priv->attendees, i); + +#if 0 + /* Check the server for free busy data */ + if (priv->client) { + GList *fb_data, *users = NULL, *l; + time_t start, end, now = time (NULL); + + start = now - 60 * 60 * 24; + end = time_add_week (now, 6); + + for (i = 0; i < priv->attendees->len; i++) { + EMeetingAttendee *ia = g_ptr_array_index (priv->attendees, i); + const char *user; + + user = itip_strip_mailto (e_meeting_attendee_get_address (ia)); + users = g_list_append (users, g_strdup (user)); + } + + fb_data = cal_client_get_free_busy (priv->client, users, start, end); + + g_list_foreach (users, (GFunc)g_free, NULL); + g_list_free (users); + + for (l = fb_data; l != NULL; l = l->next) { + CalComponent *comp = l->data; + EMeetingAttendee *ia = NULL; + CalComponentOrganizer org; + + /* Process the data for any attendees found */ + cal_component_get_organizer (comp, &org); + for (i = 0; i < priv->attendees->len; i++) { + ia = g_ptr_array_index (priv->attendees, i); + if (org.value && !strcmp (org.value, e_meeting_attendee_get_address (ia))) { + g_ptr_array_remove_fast (not_found, ia); + break; + } + ia = NULL; + } + + if (ia != NULL) + process_free_busy (im, ia, cal_component_get_as_string (comp)); + } + } +#endif + /* Look for fburl's of attendee with no free busy info on server */ + if (!priv->book_loaded) { + priv->book_load_wait = TRUE; + gtk_main (); + } + + string = g_string_new ("(or "); + for (i = 0; i < not_found->len; i++) { + EMeetingAttendee *ia = g_ptr_array_index (not_found, i); + char *query; + + if (!e_meeting_attendee_is_set_address (ia)) + continue; + + e_meeting_attendee_clear_busy_periods (ia); + + query = g_strdup_printf ("(contains \"email\" \"%s\")", itip_strip_mailto (e_meeting_attendee_get_address (ia))); + g_string_append (string, query); + g_free (query); + } + g_string_append_c (string, ')'); + + e_book_get_cursor (priv->ebook, string->str, cursor_cb, im); + + g_ptr_array_free (not_found, FALSE); + g_string_free (string, TRUE); +} + +ETableScrolled * +e_meeting_model_etable_from_model (EMeetingModel *im, const gchar *spec_file, const gchar *state_file) +{ + g_return_val_if_fail (im != NULL, NULL); + g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); + + return build_etable (E_TABLE_MODEL (im), spec_file, state_file); +} + +static void +attendee_changed_cb (EMeetingAttendee *ia, gpointer data) +{ + EMeetingModel *im = E_MEETING_MODEL (data); + EMeetingModelPrivate *priv; + gint row = -1, i; + + priv = im->priv; + + for (i = 0; i < priv->attendees->len; i++) { + if (ia == g_ptr_array_index (priv->attendees, i)) { + row = 1; + break; + } + } + + if (row == -1) + return; + + e_table_model_row_changed (E_TABLE_MODEL (im), row); +} |