/* * 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 * * * Authors: * Mike Kestner * JP Rosevear * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "calendar-config.h" #include "e-meeting-list-view.h" #include "itip-utils.h" #include #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); }