diff options
Diffstat (limited to 'calendar/gui/e-meeting-list-view.c')
-rw-r--r-- | calendar/gui/e-meeting-list-view.c | 1110 |
1 files changed, 1110 insertions, 0 deletions
diff --git a/calendar/gui/e-meeting-list-view.c b/calendar/gui/e-meeting-list-view.c new file mode 100644 index 0000000000..e211d21f82 --- /dev/null +++ b/calendar/gui/e-meeting-list-view.c @@ -0,0 +1,1110 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Mike Kestner <mkestner@ximian.com> + * JP Rosevear <jpr@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <libecal/libecal.h> +#include <libebook/libebook.h> + +#include "calendar-config.h" +#include "e-meeting-list-view.h" +#include "itip-utils.h" +#include <shell/e-shell.h> +#include "e-select-names-renderer.h" + +#define E_MEETING_LIST_VIEW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MEETING_LIST_VIEW, EMeetingListViewPrivate)) + +struct _EMeetingListViewPrivate { + EMeetingStore *store; + + ENameSelector *name_selector; + + GHashTable *renderers; +}; + +#define BUF_SIZE 1024 + +/* Signal IDs */ +enum { + ATTENDEE_ADDED, + LAST_SIGNAL +}; + +static guint e_meeting_list_view_signals[LAST_SIGNAL] = { 0 }; + +static void name_selector_dialog_close_cb (ENameSelectorDialog *dialog, gint response, gpointer data); + +static const gchar *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}; + +G_DEFINE_TYPE (EMeetingListView, e_meeting_list_view, GTK_TYPE_TREE_VIEW) + +static void +e_meeting_list_view_finalize (GObject *object) +{ + EMeetingListViewPrivate *priv; + + priv = E_MEETING_LIST_VIEW_GET_PRIVATE (object); + + if (priv->name_selector) { + e_name_selector_cancel_loading (priv->name_selector); + g_object_unref (priv->name_selector); + priv->name_selector = NULL; + } + + if (priv->renderers) { + g_hash_table_destroy (priv->renderers); + priv->renderers = NULL; + } + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_meeting_list_view_parent_class)->finalize (object); +} + +static void +e_meeting_list_view_class_init (EMeetingListViewClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EMeetingListViewPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_meeting_list_view_finalize; + + e_meeting_list_view_signals[ATTENDEE_ADDED] = g_signal_new ( + "attendee_added", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EMeetingListViewClass, attendee_added), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); +} + +static void +add_section (ENameSelector *name_selector, + const gchar *name) +{ + ENameSelectorModel *name_selector_model; + + name_selector_model = e_name_selector_peek_model (name_selector); + e_name_selector_model_add_section (name_selector_model, name, gettext (name), NULL); +} + +static void +meeting_list_view_realize_cb (EMeetingListView *view) +{ + g_return_if_fail (view != NULL); + g_return_if_fail (view->priv != NULL); + + g_signal_handlers_disconnect_by_func (view, meeting_list_view_realize_cb, NULL); + + e_name_selector_load_books (view->priv->name_selector); +} + +static void +e_meeting_list_view_init (EMeetingListView *view) +{ + ENameSelectorDialog *name_selector_dialog; + EClientCache *client_cache; + EShell *shell; + gint i; + + view->priv = E_MEETING_LIST_VIEW_GET_PRIVATE (view); + + view->priv->renderers = g_hash_table_new (g_direct_hash, g_int_equal); + + /* FIXME Refactor this so we don't need e_shell_get_default(). */ + shell = e_shell_get_default (); + client_cache = e_shell_get_client_cache (shell); + + view->priv->name_selector = e_name_selector_new (client_cache); + + for (i = 0; sections[i]; i++) + add_section (view->priv->name_selector, sections[i]); + + name_selector_dialog = + e_name_selector_peek_dialog (view->priv->name_selector); + gtk_window_set_title (GTK_WINDOW (name_selector_dialog), _("Attendees")); + g_signal_connect ( + name_selector_dialog, "response", + G_CALLBACK (name_selector_dialog_close_cb), view); + + /* postpone name_selector loading, do that only when really needed */ + g_signal_connect ( + view, "realize", + G_CALLBACK (meeting_list_view_realize_cb), NULL); +} + +static GList * +get_type_strings (void) +{ + GList *strings = NULL; + + strings = g_list_append (strings, (gchar *) _("Individual")); + strings = g_list_append (strings, (gchar *) _("Group")); + strings = g_list_append (strings, (gchar *) _("Resource")); + strings = g_list_append (strings, (gchar *) _("Room")); + strings = g_list_append (strings, (gchar *) _("Unknown")); + + return strings; +} + +static GList * +get_role_strings (void) +{ + GList *strings = NULL; + + strings = g_list_append (strings, (gchar *) _("Chair")); + strings = g_list_append (strings, (gchar *) _("Required Participant")); + strings = g_list_append (strings, (gchar *) _("Optional Participant")); + strings = g_list_append (strings, (gchar *) _("Non-Participant")); + strings = g_list_append (strings, (gchar *) _("Unknown")); + + return strings; +} + +static GList * +get_rsvp_strings (void) +{ + GList *strings = NULL; + + strings = g_list_append (strings, (gchar *) _("Yes")); + strings = g_list_append (strings, (gchar *) _("No")); + + return strings; +} + +static GList * +get_status_strings (void) +{ + GList *strings = NULL; + + strings = g_list_append (strings, (gchar *) _("Needs Action")); + strings = g_list_append (strings, (gchar *) _("Accepted")); + strings = g_list_append (strings, (gchar *) _("Declined")); + strings = g_list_append (strings, (gchar *) _("Tentative")); + strings = g_list_append (strings, (gchar *) _("Delegated")); + + return strings; +} + +static void +value_edited (GtkTreeView *view, + gint col, + const gchar *path, + const gchar *text) +{ + EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view)); + GtkTreePath *treepath = gtk_tree_path_new_from_string (path); + gint row = gtk_tree_path_get_indices (treepath)[0]; + + e_meeting_store_set_value (model, row, col, text); + gtk_tree_path_free (treepath); +} + +static guint +get_index_from_role (icalparameter_role role) +{ + switch (role) { + case ICAL_ROLE_CHAIR: + return 0; + case ICAL_ROLE_REQPARTICIPANT: + return 1; + case ICAL_ROLE_OPTPARTICIPANT: + return 2; + case ICAL_ROLE_NONPARTICIPANT: + return 3; + default: + return 1; + } +} + +void +e_meeting_list_view_add_attendee_to_name_selector (EMeetingListView *view, + EMeetingAttendee *ma) +{ + EDestinationStore *destination_store; + ENameSelectorModel *name_selector_model; + EDestination *des; + EMeetingListViewPrivate *priv; + guint i = 1; + + priv = view->priv; + + name_selector_model = e_name_selector_peek_model (priv->name_selector); + i = get_index_from_role (e_meeting_attendee_get_role (ma)); + e_name_selector_model_peek_section ( + name_selector_model, sections[i], + NULL, &destination_store); + des = e_destination_new (); + e_destination_set_email (des, itip_strip_mailto (e_meeting_attendee_get_address (ma))); + e_destination_set_name (des, e_meeting_attendee_get_cn (ma)); + e_destination_store_append_destination (destination_store, des); + g_object_unref (des); +} + +void +e_meeting_list_view_remove_attendee_from_name_selector (EMeetingListView *view, + EMeetingAttendee *ma) +{ + GList *destinations, *l; + EDestinationStore *destination_store; + ENameSelectorModel *name_selector_model; + const gchar *madd = NULL; + EMeetingListViewPrivate *priv; + guint i = 1; + + priv = view->priv; + + name_selector_model = e_name_selector_peek_model (priv->name_selector); + i = get_index_from_role (e_meeting_attendee_get_role (ma)); + e_name_selector_model_peek_section ( + name_selector_model, sections[i], + NULL, &destination_store); + destinations = e_destination_store_list_destinations (destination_store); + madd = itip_strip_mailto (e_meeting_attendee_get_address (ma)); + + for (l = destinations; l; l = g_list_next (l)) { + const gchar *attendee = NULL; + EDestination *des = l->data; + + if (e_destination_is_evolution_list (des)) { + GList *l, *dl; + + dl = (GList *) e_destination_list_get_dests (des); + + for (l = dl; l; l = l->next) { + attendee = e_destination_get_email (l->data); + if (madd && attendee && g_str_equal (madd, attendee)) { + g_object_unref (l->data); + l = g_list_remove (l, l->data); + break; + } + } + } else { + attendee = e_destination_get_email (des); + if (madd && attendee && g_str_equal (madd, attendee)) { + e_destination_store_remove_destination (destination_store, des); + } + } + } + + g_list_free (destinations); +} + +void +e_meeting_list_view_remove_all_attendees_from_name_selector (EMeetingListView *view) +{ + ENameSelectorModel *name_selector_model; + EMeetingListViewPrivate *priv; + guint i; + + priv = view->priv; + + name_selector_model = e_name_selector_peek_model (priv->name_selector); + + for (i = 0; sections[i] != NULL; i++) { + EDestinationStore *destination_store = NULL; + GList *destinations = NULL, *l = NULL; + + e_name_selector_model_peek_section ( + name_selector_model, sections[i], + NULL, &destination_store); + if (!destination_store) { + g_warning ("destination store is NULL\n"); + continue; + } + + destinations = e_destination_store_list_destinations (destination_store); + for (l = destinations; l; l = g_list_next (l)) { + EDestination *des = l->data; + + if (e_destination_is_evolution_list (des)) { + GList *m, *dl; + + dl = (GList *) e_destination_list_get_dests (des); + + for (m = dl; m; m = m->next) { + g_object_unref (m->data); + m = g_list_remove (m, l->data); + } + } else { + e_destination_store_remove_destination (destination_store, des); + } + } + g_list_free (destinations); + } +} + +static void +attendee_edited_cb (GtkCellRenderer *renderer, + const gchar *path, + GList *addresses, + GList *names, + GtkTreeView *view) +{ + EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view)); + GtkTreePath *treepath = gtk_tree_path_new_from_string (path); + gint row = gtk_tree_path_get_indices (treepath)[0]; + EMeetingAttendee *existing_attendee; + + existing_attendee = e_meeting_store_find_attendee_at_row (model, row); + + if (g_list_length (addresses) > 1) { + EMeetingAttendee *attendee; + GList *l, *m; + gboolean can_remove = TRUE; + + for (l = addresses, m = names; l && m; l = l->next, m = m->next) { + gchar *name = m->data, *email = l->data; + + if (!((name && *name) || (email && *email))) + continue; + + attendee = e_meeting_store_find_attendee (model, email, NULL); + if (attendee != NULL) { + if (attendee == existing_attendee) + can_remove = FALSE; + continue; + } + + attendee = e_meeting_store_add_attendee_with_defaults (model); + e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", (gchar *) l->data)); + e_meeting_attendee_set_cn (attendee, g_strdup (m->data)); + if (existing_attendee) { + /* FIXME Should we copy anything else? */ + e_meeting_attendee_set_cutype (attendee, e_meeting_attendee_get_cutype (existing_attendee)); + e_meeting_attendee_set_role (attendee, e_meeting_attendee_get_role (existing_attendee)); + e_meeting_attendee_set_rsvp (attendee, e_meeting_attendee_get_rsvp (existing_attendee)); + e_meeting_attendee_set_status (attendee, ICAL_PARTSTAT_NEEDSACTION); + e_meeting_attendee_set_delfrom (attendee, (gchar *) e_meeting_attendee_get_delfrom (existing_attendee)); + } + e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee); + g_signal_emit_by_name (view, "attendee_added", (gpointer) attendee); + } + + if (existing_attendee && can_remove) { + e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee); + e_meeting_store_remove_attendee (model, existing_attendee); + } + } else if (g_list_length (addresses) == 1) { + gchar *name = names->data, *email = addresses->data; + gint existing_row; + + if (!((name && *name) || (email && *email)) || ((e_meeting_store_find_attendee (model, email, &existing_row) != NULL) && existing_row != row)) { + if (existing_attendee) { + e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee); + e_meeting_store_remove_attendee (model, existing_attendee); + } + } else { + gboolean address_changed = FALSE; + EMeetingAttendee *attendee; + + if (existing_attendee) { + const gchar *addr = e_meeting_attendee_get_address (existing_attendee); + + if (addr && g_ascii_strncasecmp (addr, "MAILTO:", 7) == 0) + addr += 7; + + address_changed = addr && g_ascii_strcasecmp (addr, email) != 0; + + e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee); + attendee = existing_attendee; + } else { + attendee = e_meeting_store_add_attendee_with_defaults (model); + } + + value_edited (view, E_MEETING_STORE_ADDRESS_COL, path, email); + value_edited (view, E_MEETING_STORE_CN_COL, path, name); + + e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", email)); + e_meeting_attendee_set_cn (attendee, g_strdup (name)); + e_meeting_attendee_set_role (attendee, ICAL_ROLE_REQPARTICIPANT); + e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee); + + if (address_changed) + e_meeting_attendee_set_status (attendee, ICAL_PARTSTAT_NEEDSACTION); + + g_signal_emit_by_name (view, "attendee_added", (gpointer) attendee); + } + } else if (existing_attendee) { + const gchar *address = e_meeting_attendee_get_address (existing_attendee); + + if (!(address && *address)) { + e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee); + e_meeting_store_remove_attendee (model, existing_attendee); + } + } + + gtk_tree_path_free (treepath); +} + +static void +attendee_editing_canceled_cb (GtkCellRenderer *renderer, + GtkTreeView *view) +{ + EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view)); + GtkTreePath *path; + EMeetingAttendee *existing_attendee; + gint row; + + /* This is for newly added attendees when the editing is cancelled */ + gtk_tree_view_get_cursor (view, &path, NULL); + if (!path) + return; + + row = gtk_tree_path_get_indices (path)[0]; + existing_attendee = e_meeting_store_find_attendee_at_row (model, row); + if (existing_attendee) { + if (!e_meeting_attendee_is_set_cn (existing_attendee) && !e_meeting_attendee_is_set_address (existing_attendee)) + e_meeting_store_remove_attendee (model, existing_attendee); + } + + gtk_tree_path_free (path); +} + +static void +type_edited_cb (GtkCellRenderer *renderer, + const gchar *path, + const gchar *text, + GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_TYPE_COL, path, text); +} + +static void +role_edited_cb (GtkCellRenderer *renderer, + const gchar *path, + const gchar *text, + GtkTreeView *view) +{ + /* This is a little more complex than the other callbacks because + * we also need to update the "Required Participants" dialog. */ + + EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view)); + GtkTreePath *treepath = gtk_tree_path_new_from_string (path); + gint row = gtk_tree_path_get_indices (treepath)[0]; + EMeetingAttendee *attendee; + + attendee = e_meeting_store_find_attendee_at_row (model, row); + e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), attendee); + e_meeting_store_set_value (model, row, E_MEETING_STORE_ROLE_COL, text); + e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee); + + gtk_tree_path_free (treepath); +} + +static void +rsvp_edited_cb (GtkCellRenderer *renderer, + const gchar *path, + const gchar *text, + GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_RSVP_COL, path, text); +} + +static void +status_edited_cb (GtkCellRenderer *renderer, + const gchar *path, + const gchar *text, + GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_STATUS_COL, path, text); +} + +static void +ense_update (GtkWidget *w, + gpointer data1, + gpointer user_data) +{ + gtk_cell_editable_editing_done ((GtkCellEditable *) w); +} + +static void +editing_started_cb (GtkCellRenderer *renderer, + GtkCellEditable *editable, + gchar *path, + gpointer user_data) +{ + g_signal_connect ( + editable, "updated", + G_CALLBACK (ense_update), NULL); +} + +static GtkCellRenderer * +create_combo_cell_renderer (GList *strings) +{ + GList *li; + GtkTreeIter iter; + GtkListStore *store; + GtkCellRenderer *renderer; + + store = gtk_list_store_new (1, G_TYPE_STRING); + for (li = strings; li; li = li->next) { + const gchar *str = li->data; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, str, -1); + } + + renderer = gtk_cell_renderer_combo_new (); + + g_object_set ( + G_OBJECT (renderer), + "has-entry", FALSE, + "editable", TRUE, + "model", GTK_TREE_MODEL (store), + "text-column", 0, + NULL); + + g_object_unref (store); + g_list_free (strings); + + return renderer; +} + +static void +build_table (EMeetingListView *lview) +{ + GtkCellRenderer *renderer; + GtkTreeView *view = GTK_TREE_VIEW (lview); + EMeetingListViewPrivate *priv; + EClientCache *client_cache; + GHashTable *edit_table; + GtkTreeViewColumn *col; + gint pos; + + priv = lview->priv; + edit_table = priv->renderers; + gtk_tree_view_set_headers_visible (view, TRUE); + gtk_tree_view_set_rules_hint (view, TRUE); + + client_cache = e_name_selector_ref_client_cache (priv->name_selector); + + renderer = e_select_names_renderer_new (client_cache); + g_object_set (renderer, "editable", TRUE, NULL); + /* The extra space is just a hack to occupy more space for Attendee */ + pos = gtk_tree_view_insert_column_with_attributes ( + view, -1, _("Attendee "), renderer, + "text", E_MEETING_STORE_ATTENDEE_COL, + "name", E_MEETING_STORE_CN_COL, + "email", E_MEETING_STORE_ADDRESS_COL, + "underline", E_MEETING_STORE_ATTENDEE_UNDERLINE_COL, + NULL); + col = gtk_tree_view_get_column (view, pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable (col, TRUE); + gtk_tree_view_column_set_expand (col, TRUE); + g_object_set (col, "min-width", 50, NULL); + g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_ATTENDEE_COL)); + g_signal_connect ( + renderer, "cell_edited", + G_CALLBACK (attendee_edited_cb), view); + g_signal_connect ( + renderer, "editing-canceled", + G_CALLBACK (attendee_editing_canceled_cb), view); + g_signal_connect ( + renderer, "editing-started", + G_CALLBACK (editing_started_cb), view); + + g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_ATTENDEE_COL), renderer); + + renderer = create_combo_cell_renderer (get_type_strings ()); + pos = gtk_tree_view_insert_column_with_attributes ( + view, -1, _("Type"), renderer, + "text", E_MEETING_STORE_TYPE_COL, + NULL); + col = gtk_tree_view_get_column (view, pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable (col, TRUE); + g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_TYPE_COL)); + g_signal_connect ( + renderer, "edited", + G_CALLBACK (type_edited_cb), view); + g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_TYPE_COL), renderer); + + renderer = create_combo_cell_renderer (get_role_strings ()); + pos = gtk_tree_view_insert_column_with_attributes ( + view, -1, _("Role"), renderer, + "text", E_MEETING_STORE_ROLE_COL, + NULL); + col = gtk_tree_view_get_column (view, pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable (col, TRUE); + g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_ROLE_COL)); + g_signal_connect ( + renderer, "edited", + G_CALLBACK (role_edited_cb), view); + g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_ROLE_COL), renderer); + + renderer = create_combo_cell_renderer (get_rsvp_strings ()); + /* To translators: RSVP means "please reply" */ + pos = gtk_tree_view_insert_column_with_attributes ( + view, -1, _("RSVP"), renderer, + "text", E_MEETING_STORE_RSVP_COL, + NULL); + col = gtk_tree_view_get_column (view, pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable (col, TRUE); + g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_RSVP_COL)); + g_signal_connect ( + renderer, "edited", + G_CALLBACK (rsvp_edited_cb), view); + g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_RSVP_COL), renderer); + + renderer = create_combo_cell_renderer (get_status_strings ()); + pos = gtk_tree_view_insert_column_with_attributes ( + view, -1, _("Status"), renderer, + "text", E_MEETING_STORE_STATUS_COL, + NULL); + col = gtk_tree_view_get_column (view, pos -1); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_view_column_set_reorderable (col, TRUE); + g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_STATUS_COL)); + g_signal_connect ( + renderer, "edited", + G_CALLBACK (status_edited_cb), view); + g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_STATUS_COL), renderer); + + priv->renderers = edit_table; + + g_object_unref (client_cache); +} + +static void +change_edit_cols_for_user (gpointer key, + gpointer value, + gpointer user_data) +{ + GtkCellRenderer *renderer = (GtkCellRenderer *) value; + gint key_val = GPOINTER_TO_INT (key); + + switch (key_val) { + case E_MEETING_STORE_ATTENDEE_COL: + g_object_set (renderer, "editable", FALSE, NULL); + break; + case E_MEETING_STORE_ROLE_COL: + g_object_set (renderer, "editable", FALSE, NULL); + break; + case E_MEETING_STORE_TYPE_COL: + g_object_set (renderer, "editable", FALSE, NULL); + break; + case E_MEETING_STORE_RSVP_COL: + g_object_set (renderer, "editable", TRUE, NULL); + break; + case E_MEETING_STORE_STATUS_COL: + g_object_set (renderer, "editable", TRUE, NULL); + break; + } +} + +static void +change_edit_cols_for_organizer (gpointer key, + gpointer value, + gpointer user_data) +{ + GtkCellRenderer *renderer = (GtkCellRenderer *) value; + guint edit_level = GPOINTER_TO_INT (user_data); + + g_object_set (renderer, "editable", GINT_TO_POINTER (edit_level), NULL); +} + +static void +row_activated_cb (GtkTreeSelection *selection, + EMeetingListView *view) +{ + EMeetingAttendee *existing_attendee; + EMeetingListViewPrivate *priv; + gint row; + EMeetingAttendeeEditLevel el; + gint edit_level; + GtkTreeModel *model; + GtkTreePath *path = NULL; + GList *paths = NULL; + + priv = view->priv; + + if (!(paths = gtk_tree_selection_get_selected_rows (selection, &model))) + return; + if (g_list_length (paths) > 1) + return; + path = g_list_nth_data (paths, 0); + if (!path) + return; + + row = gtk_tree_path_get_indices (path)[0]; + existing_attendee = e_meeting_store_find_attendee_at_row (priv->store, row); + el = e_meeting_attendee_get_edit_level (existing_attendee); + + switch (el) { + case E_MEETING_ATTENDEE_EDIT_NONE: + edit_level = FALSE; + g_hash_table_foreach ( + priv->renderers, + change_edit_cols_for_organizer, + GINT_TO_POINTER (edit_level)); + break; + + case E_MEETING_ATTENDEE_EDIT_FULL: + edit_level = TRUE; + g_hash_table_foreach ( + priv->renderers, + change_edit_cols_for_organizer, + GINT_TO_POINTER (edit_level)); + break; + + case E_MEETING_ATTENDEE_EDIT_STATUS: + edit_level = FALSE; + g_hash_table_foreach ( + priv->renderers, + change_edit_cols_for_user, + GINT_TO_POINTER (edit_level)); + break; + } +} + +EMeetingListView * +e_meeting_list_view_new (EMeetingStore *store) +{ + EMeetingListView *view = g_object_new (E_TYPE_MEETING_LIST_VIEW, NULL); + GtkTreeSelection *selection; + + if (view) { + view->priv->store = store; + gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (store)); + build_table (view); + } + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + g_signal_connect ( + selection, "changed", + G_CALLBACK (row_activated_cb), view); + + return view; +} + +void +e_meeting_list_view_column_set_visible (EMeetingListView *view, + EMeetingStoreColumns column, + gboolean visible) +{ + GList *cols, *l; + + cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (view)); + + for (l = cols; l; l = l->next) { + GtkTreeViewColumn *col = (GtkTreeViewColumn *) l->data; + EMeetingStoreColumns store_colum = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (col), "mtg-store-col")); + + if (store_colum == column) { + gtk_tree_view_column_set_visible (col, visible); + break; + } + } +} + +void +e_meeting_list_view_edit (EMeetingListView *emlv, + EMeetingAttendee *attendee) +{ + EMeetingListViewPrivate *priv; + GtkTreePath *path; + GtkTreeViewColumn *focus_col; + + priv = emlv->priv; + + g_return_if_fail (emlv != NULL); + g_return_if_fail (E_IS_MEETING_LIST_VIEW (emlv)); + g_return_if_fail (attendee != NULL); + + path = e_meeting_store_find_attendee_path (priv->store, attendee); + focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (emlv), 0); + + if (path) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (emlv), path, focus_col, TRUE); + + gtk_tree_path_free (path); + } +} + +static void +process_section (EMeetingListView *view, + GList *destinations, + icalparameter_role role, + GSList **la) +{ + EMeetingListViewPrivate *priv; + GList *l; + + priv = view->priv; + for (l = destinations; l; l = g_list_next (l)) { + EDestination *destination = l->data, *des = NULL; + const GList *list_dests = NULL, *l; + GList card_dest; + + if (e_destination_is_evolution_list (destination)) { + list_dests = e_destination_list_get_dests (destination); + } else { + EContact *contact = e_destination_get_contact (destination); + /* check if the contact is contact list which is not expanded yet */ + /* we expand it by getting the list again from the server forming the query */ + if (contact && e_contact_get (contact , E_CONTACT_IS_LIST)) { + EBookClient *book_client = NULL; + ENameSelectorDialog *dialog; + ENameSelectorModel *model; + EContactStore *c_store; + GSList *clients, *l; + gchar *uid = e_contact_get (contact, E_CONTACT_BOOK_UID); + + dialog = e_name_selector_peek_dialog (view->priv->name_selector); + model = e_name_selector_dialog_peek_model (dialog); + c_store = e_name_selector_model_peek_contact_store (model); + clients = e_contact_store_get_clients (c_store); + + for (l = clients; l; l = l->next) { + EBookClient *b = l->data; + ESource *source; + + source = e_client_get_source (E_CLIENT (b)); + + if (g_strcmp0 (uid, e_source_get_uid (source)) == 0) { + book_client = b; + break; + } + } + + if (book_client) { + GSList *contacts; + EContact *n_con = NULL; + gchar *query; + + query = g_strdup_printf ( + "(is \"full_name\" \"%s\")", + (gchar *) e_contact_get (contact, E_CONTACT_FULL_NAME)); + + if (!e_book_client_get_contacts_sync (book_client, query, &contacts, NULL, NULL)) { + g_warning ("Could not get contact from the book \n"); + g_free (query); + g_slist_free (clients); + return; + } else { + des = e_destination_new (); + n_con = contacts->data; + + e_destination_set_contact (des, n_con, 0); + e_destination_set_client (des, book_client); + list_dests = e_destination_list_get_dests (des); + + g_slist_foreach (contacts, (GFunc) g_object_unref, NULL); + g_slist_free (contacts); + } + + g_free (query); + } + g_slist_free (clients); + } else { + card_dest.next = NULL; + card_dest.prev = NULL; + card_dest.data = destination; + list_dests = &card_dest; + } + } + + for (l = list_dests; l; l = l->next) { + EDestination *dest = l->data; + EContact *contact; + const gchar *name, *attendee = NULL; + gchar *fburi = NULL; + + name = e_destination_get_name (dest); + attendee = e_destination_get_email (dest); + + if (attendee == NULL || *attendee == '\0') + continue; + + contact = e_destination_get_contact (dest); + if (contact) + fburi = e_contact_get (contact, E_CONTACT_FREEBUSY_URL); + + if (e_meeting_store_find_attendee (priv->store, attendee, NULL) == NULL) { + EMeetingAttendee *ia = e_meeting_store_add_attendee_with_defaults (priv->store); + + 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)); + + if (fburi) + e_meeting_attendee_set_fburi (ia, fburi); + } else { + if (g_slist_length (*la) == 1) { + g_slist_free (*la); + *la = NULL; + } else + *la = g_slist_remove_link (*la, g_slist_find_custom (*la, attendee, (GCompareFunc)g_ascii_strcasecmp)); + } + } + + if (des) { + g_object_unref (des); + des = NULL; + } + + } +} + +static void +add_to_list (gpointer data, + gpointer u_data) +{ + GSList **user_data = u_data; + + *user_data = g_slist_append (*user_data, (gpointer)itip_strip_mailto (e_meeting_attendee_get_address (data))); +} + +static void +name_selector_dialog_close_cb (ENameSelectorDialog *dialog, + gint response, + gpointer data) +{ + EMeetingListView *view = E_MEETING_LIST_VIEW (data); + ENameSelectorModel *name_selector_model; + EMeetingStore *store; + const GPtrArray *attendees; + gint i; + GSList *la = NULL, *l; + + name_selector_model = e_name_selector_peek_model (view->priv->name_selector); + store = E_MEETING_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view))); + attendees = e_meeting_store_get_attendees (store); + + /* get all the email ids of the attendees */ + g_ptr_array_foreach ((GPtrArray *) attendees, (GFunc) add_to_list, &la); + + for (i = 0; sections[i] != NULL; i++) { + EDestinationStore *destination_store; + GList *destinations; + + e_name_selector_model_peek_section ( + name_selector_model, sections[i], + NULL, &destination_store); + if (!destination_store) { + g_warning ("destination store is NULL\n"); + continue; + } + + destinations = e_destination_store_list_destinations (destination_store); + process_section (view, destinations, roles[i], &la); + g_list_free (destinations); + } + + /* remove the deleted attendees from name selector */ + for (l = la; l != NULL; l = l->next) { + EMeetingAttendee *ma = NULL; + const gchar *email = l->data; + gint i; + + ma = e_meeting_store_find_attendee (store, email, &i); + + if (ma) { + if (e_meeting_attendee_get_edit_level (ma) != E_MEETING_ATTENDEE_EDIT_FULL) + g_warning ("Not enough rights to delete attendee: %s\n", e_meeting_attendee_get_address (ma)); + else + e_meeting_store_remove_attendee (store, ma); + } + } + + g_slist_free (la); + gtk_widget_hide (GTK_WIDGET (dialog)); +} + +void +e_meeting_list_view_invite_others_dialog (EMeetingListView *view) +{ + e_name_selector_show_dialog ( + view->priv->name_selector, + GTK_WIDGET (view)); +} + +void +e_meeting_list_view_set_editable (EMeetingListView *lview, + gboolean set) +{ + EMeetingListViewPrivate *priv = lview->priv; + + gint edit_level = set; + + g_hash_table_foreach (priv->renderers, change_edit_cols_for_organizer, GINT_TO_POINTER (edit_level)); +} + +ENameSelector * +e_meeting_list_view_get_name_selector (EMeetingListView *lview) +{ + EMeetingListViewPrivate *priv; + + g_return_val_if_fail (lview != NULL, NULL); + g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (lview), NULL); + + priv = lview->priv; + + return priv->name_selector; +} + +void +e_meeting_list_view_set_name_selector (EMeetingListView *lview, + ENameSelector *name_selector) +{ + EMeetingListViewPrivate *priv; + + g_return_if_fail (lview != NULL); + g_return_if_fail (E_IS_MEETING_LIST_VIEW (lview)); + + priv = lview->priv; + + if (priv->name_selector) { + g_object_unref (priv->name_selector); + priv->name_selector = NULL; + } + + priv->name_selector = g_object_ref (name_selector); +} + |