From 54869d90bd04f0a816b1786530d3555ac2b034cc Mon Sep 17 00:00:00 2001 From: Mike Kestner Date: Tue, 30 Sep 2003 22:39:04 +0000 Subject: new VOID:STRING,STRING,STRING build the new view/store/renderer/editable 2003-09-30 Mike Kestner * cal-util/cal-util-marshal.list : new VOID:STRING,STRING,STRING * gui/Makefile.am : build the new view/store/renderer/editable * gui/e-select-names-renderer.* : new completion cell renderer * gui/e-select-names-editable.* : new completion cell editable * gui/e-meeting-model.* : killed. code reused in list-view/store * gui/e-meeting-store.* : port of EMeetingModel to GtkTreeModel * gui/e-meeting-list-view.* : GtkTreeView subclass for attendee lists * gui/e-meeting-time-sel.c : Use the new store/view * gui/e-meeting-time-sel-item.c : Use the new store/view * gui/dialogs/Makefile.am : don't install the etspec anymore. * gui/dialogs/event-editor.c : Use the new store/view. * gui/dialogs/meeting-page.c : Use the new store/view. * gui/dialogs/shedule-page.c : Use the new store/view. * gui/dialogs/task-editor.c : Use the new store/view. svn path=/trunk/; revision=22774 --- calendar/gui/e-meeting-store.c | 1317 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1317 insertions(+) create mode 100644 calendar/gui/e-meeting-store.c (limited to 'calendar/gui/e-meeting-store.c') diff --git a/calendar/gui/e-meeting-store.c b/calendar/gui/e-meeting-store.c new file mode 100644 index 0000000000..0129756e7c --- /dev/null +++ b/calendar/gui/e-meeting-store.c @@ -0,0 +1,1317 @@ +/* + * e-meeting-store.c + * + * Copyright (C) 2001-2003 Ximian, Inc. + * + * Authors: JP Rosevear + * Mike Kestner + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "calendar-config.h" +#include "itip-utils.h" +#include "e-meeting-utils.h" +#include "e-meeting-attendee.h" +#include "e-meeting-store.h" + +#define ROW_VALID(store, row) (row >= 0 && row < store->priv->attendees->len) + +struct _EMeetingStorePrivate { + GPtrArray *attendees; + gint stamp; + + CalClient *client; + icaltimezone *zone; + + EBook *ebook; + gboolean book_loaded; + gboolean book_load_wait; + + GPtrArray *refresh_queue; + GHashTable *refresh_data; + guint refresh_idle_id; +}; + +#define BUF_SIZE 1024 + +typedef struct _EMeetingStoreQueueData EMeetingStoreQueueData; +struct _EMeetingStoreQueueData { + EMeetingStore *store; + EMeetingAttendee *attendee; + + gboolean refreshing; + + EMeetingTime start; + EMeetingTime end; + + char buffer[BUF_SIZE]; + GString *string; + + GPtrArray *call_backs; + GPtrArray *data; +}; + + +static GObjectClass *parent_class = NULL; + +static void +book_open_cb (EBook *book, EBookStatus status, gpointer data) +{ + EMeetingStore *store = E_MEETING_STORE (data); + + if (status == E_BOOK_STATUS_SUCCESS) + store->priv->book_loaded = TRUE; + else + g_warning ("Book not loaded"); + + if (store->priv->book_load_wait) { + store->priv->book_load_wait = FALSE; + gtk_main_quit (); + } +} + +static void +start_addressbook_server (EMeetingStore *store) +{ + store->priv->ebook = e_book_new (); + e_book_load_default_book (store->priv->ebook, book_open_cb, store); +} + +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 GtkTreeModelFlags +get_flags (GtkTreeModel *model) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), 0); + + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static int +get_n_columns (GtkTreeModel *model) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), 0); + + return E_MEETING_STORE_COLUMN_COUNT; +} + +static GType +get_column_type (GtkTreeModel *model, int col) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), G_TYPE_INVALID); + + switch (col) { + case E_MEETING_STORE_ADDRESS_COL: + case E_MEETING_STORE_MEMBER_COL: + case E_MEETING_STORE_TYPE_COL: + case E_MEETING_STORE_ROLE_COL: + case E_MEETING_STORE_RSVP_COL: + case E_MEETING_STORE_DELTO_COL: + case E_MEETING_STORE_DELFROM_COL: + case E_MEETING_STORE_STATUS_COL: + case E_MEETING_STORE_CN_COL: + case E_MEETING_STORE_LANGUAGE_COL: + case E_MEETING_STORE_ATTENDEE_COL: + return G_TYPE_STRING; + case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL: + return PANGO_TYPE_UNDERLINE; + default: + return G_TYPE_INVALID; + } +} + +static gboolean +get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) +{ + int row; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + + row = gtk_tree_path_get_indices (path) [0]; + + if (!ROW_VALID (E_MEETING_STORE (model), row)) + return FALSE; + + iter->stamp = E_MEETING_STORE (model)->priv->stamp; + iter->user_data = GINT_TO_POINTER (row); + + return TRUE; +} + +static GtkTreePath * +get_path (GtkTreeModel *model, GtkTreeIter *iter) +{ + int row; + GtkTreePath *result; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), NULL); + g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, NULL); + + row = GPOINTER_TO_INT (iter->user_data); + + g_return_val_if_fail (ROW_VALID (E_MEETING_STORE (model), row), NULL); + + result = gtk_tree_path_new (); + gtk_tree_path_append_index (result, row); + return result; +} + +static void +get_value (GtkTreeModel *model, GtkTreeIter *iter, int col, GValue *value) +{ + EMeetingStore *store; + EMeetingAttendee *attendee; + const gchar *cn; + int row; + + g_return_if_fail (E_IS_MEETING_STORE (model)); + g_return_if_fail (col >= 0 && col < E_MEETING_STORE_COLUMN_COUNT); + + row = GPOINTER_TO_INT (iter->user_data); + store = E_MEETING_STORE (model); + + g_return_if_fail (iter->stamp == store->priv->stamp); + g_return_if_fail (ROW_VALID (E_MEETING_STORE (model), row)); + + attendee = g_ptr_array_index (store->priv->attendees, row); + + switch (col) { + case E_MEETING_STORE_ADDRESS_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_address (attendee))); + break; + case E_MEETING_STORE_MEMBER_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, e_meeting_attendee_get_member (attendee)); + break; + case E_MEETING_STORE_TYPE_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, type_to_text (e_meeting_attendee_get_cutype (attendee))); + break; + case E_MEETING_STORE_ROLE_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, role_to_text (e_meeting_attendee_get_role (attendee))); + break; + case E_MEETING_STORE_RSVP_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, boolean_to_text (e_meeting_attendee_get_rsvp (attendee))); + break; + case E_MEETING_STORE_DELTO_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_delto (attendee))); + break; + case E_MEETING_STORE_DELFROM_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_delfrom (attendee))); + break; + case E_MEETING_STORE_STATUS_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, partstat_to_text (e_meeting_attendee_get_status (attendee))); + break; + case E_MEETING_STORE_CN_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, e_meeting_attendee_get_cn (attendee)); + break; + case E_MEETING_STORE_LANGUAGE_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, e_meeting_attendee_get_language (attendee)); + break; + case E_MEETING_STORE_ATTENDEE_COL: + g_value_init (value, G_TYPE_STRING); + cn = e_meeting_attendee_get_cn (attendee); + if (strcmp (cn, "")) + g_value_set_string (value, cn); + else + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_address (attendee))); + break; + case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL: + cn = e_meeting_attendee_get_cn (attendee); + g_value_init (value, PANGO_TYPE_UNDERLINE); + g_value_set_enum (value, strcmp ("", cn) == 0 ? PANGO_UNDERLINE_NONE : PANGO_UNDERLINE_SINGLE); + } +} + +static gboolean +iter_next (GtkTreeModel *model, GtkTreeIter *iter) +{ + int row; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, FALSE); + + row = GPOINTER_TO_INT (iter->user_data) + 1; + iter->user_data = GINT_TO_POINTER (row); + + return ROW_VALID (E_MEETING_STORE (model), row); +} + +static gboolean +iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent) +{ + EMeetingStore *store; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + + store = E_MEETING_STORE (model); + + if (parent || store->priv->attendees->len <= 0) + return FALSE; + + iter->stamp = store->priv->stamp; + iter->user_data = GINT_TO_POINTER (0); + + return TRUE; +} + +static gboolean +iter_has_child (GtkTreeModel *model, GtkTreeIter *iter) +{ + return FALSE; +} + +static int +iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), -1); + + if (!iter) + return E_MEETING_STORE (model)->priv->attendees->len; + + g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, -1); + + return 0; +} + +static gboolean +iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent, int n) +{ + EMeetingStore *store; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + + store = E_MEETING_STORE (model); + + if (parent || !ROW_VALID (store, n)) + return FALSE; + + iter->stamp = store->priv->stamp; + iter->user_data = GINT_TO_POINTER (n); + + return TRUE; +} + +static gboolean +iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child) +{ + return FALSE; +} + +static void +ems_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = get_flags; + iface->get_n_columns = get_n_columns; + iface->get_column_type = get_column_type; + iface->get_iter = get_iter; + iface->get_path = get_path; + iface->get_value = get_value; + iface->iter_next = iter_next; + iface->iter_children = iter_children; + iface->iter_has_child = iter_has_child; + iface->iter_n_children = iter_n_children; + iface->iter_nth_child = iter_nth_child; + iface->iter_parent = iter_parent; +} + +void +e_meeting_store_set_value (EMeetingStore *store, int row, int col, const gchar *val) +{ + icalparameter_cutype type; + EMeetingAttendee *attendee = g_ptr_array_index (store->priv->attendees, row); + + switch (col) { + case E_MEETING_STORE_ADDRESS_COL: + if (val != NULL && *((char *)val)) + e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", (char *) val)); + break; + case E_MEETING_STORE_MEMBER_COL: + e_meeting_attendee_set_member (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_TYPE_COL: + type = text_to_type (val); + e_meeting_attendee_set_cutype (attendee, text_to_type (val)); + if (type == ICAL_CUTYPE_RESOURCE) { + e_meeting_attendee_set_role (attendee, ICAL_ROLE_NONPARTICIPANT); + } + break; + case E_MEETING_STORE_ROLE_COL: + e_meeting_attendee_set_role (attendee, text_to_role (val)); + break; + case E_MEETING_STORE_RSVP_COL: + e_meeting_attendee_set_rsvp (attendee, text_to_boolean (val)); + break; + case E_MEETING_STORE_DELTO_COL: + e_meeting_attendee_set_delto (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_DELFROM_COL: + e_meeting_attendee_set_delfrom (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_STATUS_COL: + e_meeting_attendee_set_status (attendee, text_to_partstat (val)); + break; + case E_MEETING_STORE_CN_COL: + e_meeting_attendee_set_cn (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_LANGUAGE_COL: + e_meeting_attendee_set_language (attendee, g_strdup (val)); + break; + } +} + +static gboolean +is_cell_editable (EMeetingStore *etm, int col, int row) +{ + EMeetingStore *store; + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee; + EMeetingAttendeeEditLevel level; + + store = E_MEETING_STORE (etm); + priv = store->priv; + + if (col == E_MEETING_STORE_DELTO_COL + || col == E_MEETING_STORE_DELFROM_COL) + return FALSE; + + if (row == -1) + return TRUE; + if (row >= priv->attendees->len) + return TRUE; + + attendee = g_ptr_array_index (priv->attendees, row); + level = e_meeting_attendee_get_edit_level (attendee); + + switch (level) { + case E_MEETING_ATTENDEE_EDIT_FULL: + return TRUE; + case E_MEETING_ATTENDEE_EDIT_STATUS: + return col == E_MEETING_STORE_STATUS_COL; + case E_MEETING_ATTENDEE_EDIT_NONE: + return FALSE; + } + + return TRUE; +} + +static void +refresh_queue_remove (EMeetingStore *store, EMeetingAttendee *attendee) +{ + EMeetingStorePrivate *priv; + EMeetingStoreQueueData *qdata; + + priv = store->priv; + + /* Free the queue data */ + qdata = g_hash_table_lookup (priv->refresh_data, attendee); + if (qdata) { + g_hash_table_remove (priv->refresh_data, attendee); + 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, attendee); + g_object_unref (attendee); +} + +static void +ems_finalize (GObject *obj) +{ + EMeetingStore *store = E_MEETING_STORE (obj); + EMeetingStorePrivate *priv; + int i; + + priv = store->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); + + if (priv->client != NULL) + g_object_unref (priv->client); + + if (priv->ebook != NULL) + g_object_unref (priv->ebook); + + while (priv->refresh_queue->len > 0) + refresh_queue_remove (store, 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); +} + +static void +ems_class_init (GObjectClass *klass) +{ + parent_class = g_type_class_peek_parent (klass); + + klass->finalize = ems_finalize; +} + + +static void +ems_init (EMeetingStore *store) +{ + EMeetingStorePrivate *priv; + + priv = g_new0 (EMeetingStorePrivate, 1); + + store->priv = priv; + + priv->attendees = g_ptr_array_new (); + + priv->zone = icaltimezone_get_builtin_timezone (calendar_config_get_timezone ()); + + priv->refresh_queue = g_ptr_array_new (); + priv->refresh_data = g_hash_table_new (g_direct_hash, g_direct_equal); + + start_addressbook_server (store); +} + +GType +e_meeting_store_get_type (void) +{ + static GType ems_type = 0; + + if (!ems_type) { + static const GTypeInfo ems_info = { + sizeof (GtkListStoreClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) ems_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkListStore), + 0, + (GInstanceInitFunc) ems_init }; + + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) ems_tree_model_init, + NULL, + NULL }; + + ems_type = g_type_register_static (G_TYPE_OBJECT, + "EMeetingStore", + &ems_info, 0); + + g_type_add_interface_static (ems_type, + GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return ems_type; +} + +GObject * +e_meeting_store_new (void) +{ + return g_object_new (E_TYPE_MEETING_STORE, NULL); +} + + +CalClient * +e_meeting_store_get_cal_client (EMeetingStore *store) +{ + return store->priv->client; +} + +void +e_meeting_store_set_cal_client (EMeetingStore *store, CalClient *client) +{ + if (store->priv->client != NULL) + g_object_unref (store->priv->client); + + if (client != NULL) + g_object_ref (client); + store->priv->client = client; +} + +icaltimezone * +e_meeting_store_get_zone (EMeetingStore *store) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL); + + return store->priv->zone; +} + +void +e_meeting_store_set_zone (EMeetingStore *store, icaltimezone *zone) +{ + g_return_if_fail (E_IS_MEETING_STORE (store)); + + store->priv->zone = zone; +} + +static void +attendee_changed_cb (EMeetingAttendee *attendee, gpointer data) +{ + EMeetingStore *store = E_MEETING_STORE (data); + GtkTreePath *path; + GtkTreeIter iter; + gint row = -1, i; + + for (i = 0; i < store->priv->attendees->len; i++) { + if (attendee == g_ptr_array_index (store->priv->attendees, i)) { + row = i; + break; + } + } + + if (row == -1) + return; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, row); + get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, &iter); + gtk_tree_path_free (path); +} + +void +e_meeting_store_add_attendee (EMeetingStore *store, EMeetingAttendee *attendee) +{ + GtkTreePath *path; + GtkTreeIter iter; + + g_return_if_fail (E_IS_MEETING_STORE (store)); + + g_object_ref (attendee); + g_ptr_array_add (store->priv->attendees, attendee); + + g_signal_connect (attendee, "changed", G_CALLBACK (attendee_changed_cb), store); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, store->priv->attendees->len - 1); + get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), path, &iter); + gtk_tree_path_free (path); +} + +EMeetingAttendee * +e_meeting_store_add_attendee_with_defaults (EMeetingStore *store) +{ + EMeetingAttendee *attendee; + char *str; + + attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new ()); + + e_meeting_attendee_set_address (attendee, g_strdup ("")); + e_meeting_attendee_set_member (attendee, g_strdup ("")); + + str = g_strdup (_("Individual")); + e_meeting_attendee_set_cutype (attendee, text_to_type (str)); + g_free (str); + str = g_strdup (_("Required Participant")); + e_meeting_attendee_set_role (attendee, text_to_role (str)); + g_free (str); + str = g_strdup (_("Yes")); + e_meeting_attendee_set_rsvp (attendee, text_to_boolean (str)); + g_free (str); + + e_meeting_attendee_set_delto (attendee, g_strdup ("")); + e_meeting_attendee_set_delfrom (attendee, g_strdup ("")); + + str = g_strdup (_("Needs Action")); + e_meeting_attendee_set_status (attendee, text_to_partstat (str)); + g_free (str); + + e_meeting_attendee_set_cn (attendee, g_strdup ("")); + e_meeting_attendee_set_language (attendee, g_strdup ("en")); + + e_meeting_store_add_attendee (store, attendee); + + return attendee; +} + +void +e_meeting_store_remove_attendee (EMeetingStore *store, EMeetingAttendee *attendee) +{ + gint i, row = -1; + GtkTreePath *path; + + for (i = 0; i < store->priv->attendees->len; i++) { + if (attendee == g_ptr_array_index (store->priv->attendees, i)) { + row = i; + break; + } + } + + if (row != -1) { + g_ptr_array_remove_index (store->priv->attendees, row); + g_object_unref (attendee); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, row); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path); + gtk_tree_path_free (path); + } +} + +void +e_meeting_store_remove_all_attendees (EMeetingStore *store) +{ + gint i; + GtkTreePath *path = gtk_tree_path_new (); + + gtk_tree_path_append_index (path, 0); + + for (i = 0; i < store->priv->attendees->len; i++) { + EMeetingAttendee *attendee = g_ptr_array_index (store->priv->attendees, i); + g_object_unref (attendee); + + gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path); + gtk_tree_path_next (path); + } + + g_ptr_array_set_size (store->priv->attendees, 0); + gtk_tree_path_free (path); +} + +EMeetingAttendee * +e_meeting_store_find_attendee (EMeetingStore *store, const gchar *address, gint *row) +{ + EMeetingAttendee *attendee; + int i; + + if (address == NULL) + return NULL; + + for (i = 0; i < store->priv->attendees->len; i++) { + const gchar *attendee_address; + + attendee = g_ptr_array_index (store->priv->attendees, i); + + attendee_address = e_meeting_attendee_get_address (attendee); + if (attendee_address && !g_strcasecmp (itip_strip_mailto (attendee_address), itip_strip_mailto (address))) { + if (row != NULL) + *row = i; + + return attendee; + } + } + + return NULL; +} + +EMeetingAttendee * +e_meeting_store_find_attendee_at_row (EMeetingStore *store, gint row) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL); + g_return_val_if_fail (ROW_VALID (store, row), NULL); + + return g_ptr_array_index (store->priv->attendees, row); +} + +gint +e_meeting_store_count_actual_attendees (EMeetingStore *store) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), 0); + + return store->priv->attendees->len; +} + +const GPtrArray * +e_meeting_store_get_attendees (EMeetingStore *store) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL); + + return store->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; + + 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 +process_callbacks (EMeetingStoreQueueData *qdata) +{ + EMeetingStore *store; + int i; + + for (i = 0; i < qdata->call_backs->len; i++) { + EMeetingStoreRefreshCallback 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); + } + + store = qdata->store; + refresh_queue_remove (qdata->store, qdata->attendee); + g_object_unref (store); +} + +static void +process_free_busy_comp (EMeetingAttendee *attendee, + 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 (attendee, + 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 (attendee, + 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 (attendee, + 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 (EMeetingStoreQueueData *qdata, char *text) +{ + EMeetingStore *store = qdata->store; + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee = qdata->attendee; + icalcomponent *main_comp; + icalcomponent_kind kind = ICAL_NO_COMPONENT; + + priv = store->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 (attendee, 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 (attendee, main_comp, priv->zone, NULL); + } + + icalcomponent_free (main_comp); + + process_callbacks (qdata); +} + +static gboolean +refresh_busy_periods (gpointer data) +{ + EMeetingStore *store = E_MEETING_STORE (data); + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee = NULL; + EMeetingStoreQueueData *qdata = NULL; + char *query; + int i; + + priv = store->priv; + + /* Check to see if there are any remaining attendees in the queue */ + for (i = 0; i < priv->refresh_queue->len; i++) { + attendee = g_ptr_array_index (priv->refresh_queue, i); + g_assert (attendee != NULL); + + qdata = g_hash_table_lookup (priv->refresh_data, attendee); + if (!qdata) + continue; + + 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->store); + + /* Check the server for free busy data */ + if (priv->client) { + GList *fb_data, *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 (attendee)); + users = g_list_append (users, g_strdup (user)); + fb_data = cal_client_get_free_busy (priv->client, users, startt, endt); + + 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 (attendee)) { + process_callbacks (qdata); + return TRUE; + } + + query = g_strdup_printf ("(contains \"email\" \"%s\")", + itip_strip_mailto (e_meeting_attendee_get_address (attendee))); + process_callbacks (qdata); + g_free (query); + + return TRUE; +} + +static void +refresh_queue_add (EMeetingStore *store, int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data) +{ + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee; + EMeetingStoreQueueData *qdata; + + priv = store->priv; + + attendee = g_ptr_array_index (priv->attendees, row); + if (attendee == NULL) + return; + + qdata = g_hash_table_lookup (priv->refresh_data, attendee); + if (qdata == NULL) { + qdata = g_new0 (EMeetingStoreQueueData, 1); + + qdata->store = store; + qdata->attendee = attendee; + e_meeting_attendee_clear_busy_periods (attendee); + e_meeting_attendee_set_has_calendar_info (attendee, 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, attendee, 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 (attendee); + g_ptr_array_add (priv->refresh_queue, attendee); + + if (priv->refresh_idle_id == 0) + priv->refresh_idle_id = g_idle_add (refresh_busy_periods, store); +} + +static void +async_close (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer data) +{ + EMeetingStoreQueueData *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) +{ + EMeetingStoreQueueData *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); +} + +void +e_meeting_store_refresh_all_busy_periods (EMeetingStore *store, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data) +{ + int i; + + g_return_if_fail (E_IS_MEETING_STORE (store)); + + for (i = 0; i < store->priv->attendees->len; i++) + refresh_queue_add (store, i, start, end, call_back, data); +} + +void +e_meeting_store_refresh_busy_periods (EMeetingStore *store, + int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data) +{ + g_return_if_fail (E_IS_MEETING_STORE (store)); + + refresh_queue_add (store, row, start, end, call_back, data); +} + -- cgit v1.2.3