1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-name-selector-dialog.c - Dialog that lets user pick EDestinations.
5 * Copyright (C) 2004 Novell, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * Author: Hans Petter Jansson <hpj@novell.com>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtkalignment.h>
29 #include <gtk/gtkbutton.h>
30 #include <gtk/gtkentry.h>
31 #include <gtk/gtklabel.h>
32 #include <gtk/gtkscrolledwindow.h>
33 #include <gtk/gtkstock.h>
34 #include <glib/gi18n-lib.h>
35 #include <libedataserverui/e-source-option-menu.h>
36 #include <libedataserverui/e-destination-store.h>
37 #include <libedataserverui/e-contact-store.h>
38 #include <libedataserverui/e-book-auth-util.h>
39 #include "libedataserver/e-sexp.h"
40 #include "libedataserver/e-categories.h"
41 #include "libedataserver/libedataserver-private.h"
42 #include "e-name-selector-dialog.h"
49 GtkButton *transfer_button;
50 GtkButton *remove_button;
51 GtkTreeView *destination_view;
58 ENameSelectorDialog *dlg_ptr;
61 typedef struct _ENameSelectorDialogPrivate ENameSelectorDialogPrivate;
62 struct _ENameSelectorDialogPrivate
64 guint destination_index;
67 static ESource *find_first_source (ESourceList *source_list);
68 static void search_changed (ENameSelectorDialog *name_selector_dialog);
69 static void source_selected (ENameSelectorDialog *name_selector_dialog, ESource *source);
70 static void transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *transfer_button);
71 static void contact_selection_changed (ENameSelectorDialog *name_selector_dialog);
72 static void setup_name_selector_model (ENameSelectorDialog *name_selector_dialog);
73 static void shutdown_name_selector_model (ENameSelectorDialog *name_selector_dialog);
74 static void contact_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path);
75 static void destination_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path,
76 GtkTreeViewColumn *column, GtkTreeView *tree_view);
77 static gboolean destination_key_press (ENameSelectorDialog *name_selector_dialog, GdkEventKey *event, GtkTreeView *tree_view);
78 static void remove_button_clicked (GtkButton *button, SelData *data);
79 static void remove_books (ENameSelectorDialog *name_selector_dialog);
80 static void contact_column_formatter (GtkTreeViewColumn *column, GtkCellRenderer *cell,
81 GtkTreeModel *model, GtkTreeIter *iter,
82 ENameSelectorDialog *name_selector_dialog);
83 static void destination_column_formatter (GtkTreeViewColumn *column, GtkCellRenderer *cell,
84 GtkTreeModel *model, GtkTreeIter *iter,
85 ENameSelectorDialog *name_selector_dialog);
87 /* ------------------ *
88 * Class/object setup *
89 * ------------------ */
91 G_DEFINE_TYPE (ENameSelectorDialog, e_name_selector_dialog, GTK_TYPE_DIALOG);
93 #define E_NAME_SELECTOR_DIALOG_GET_PRIVATE(obj) \
94 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), E_TYPE_NAME_SELECTOR_DIALOG, ENameSelectorDialogPrivate))
98 e_name_selector_dialog_get_property (GObject *object, guint prop_id,
99 GValue *value, GParamSpec *pspec)
104 e_name_selector_dialog_set_property (GObject *object, guint prop_id,
105 const GValue *value, GParamSpec *pspec)
109 /* FIXME: category_list should become part of ENameSelectorDialog structure */
110 GList *category_list;
113 e_name_selector_dialog_populate_categories (ENameSelectorDialog *name_selector_dialog)
115 GtkWidget *category_option_menu;
116 GtkWidget *category_menu;
118 category_option_menu = glade_xml_get_widget (name_selector_dialog->gui, "optionmenu-category");
120 /* Categories are already sorted */
121 category_list = e_categories_get_list () ;
122 category_list = g_list_prepend (category_list, _("Any Category"));
124 category_menu = gtk_menu_new ();
128 item = gtk_menu_item_new_with_label (l->data);
129 gtk_menu_shell_append (GTK_MENU_SHELL (category_menu), item);
132 gtk_widget_show_all (category_menu);
133 gtk_option_menu_set_menu (GTK_OPTION_MENU (category_option_menu), category_menu);
135 g_signal_connect_swapped (category_option_menu, "changed", G_CALLBACK (search_changed), name_selector_dialog);
139 e_name_selector_dialog_init (ENameSelectorDialog *name_selector_dialog)
141 GtkTreeSelection *contact_selection;
142 GtkTreeViewColumn *column;
143 GtkCellRenderer *cell_renderer;
145 GtkWidget *container;
147 GtkTreeSelection *selection;
148 ESourceList *source_list;
150 GConfClient *gconf_client;
153 ENameSelectorDialogPrivate *priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (name_selector_dialog);
154 priv->destination_index = 0;
157 gladefile = g_build_filename (E_DATA_SERVER_UI_GLADEDIR,
158 "e-name-selector-dialog.glade",
160 name_selector_dialog->gui = glade_xml_new (gladefile, NULL, GETTEXT_PACKAGE);
163 widget = glade_xml_get_widget (name_selector_dialog->gui, "name-selector-box");
165 g_warning ("ENameSelectorDialog can't load Glade interface!");
166 g_object_unref (name_selector_dialog->gui);
167 name_selector_dialog->gui = NULL;
171 /* Get addressbook sources */
173 if (!e_book_get_addressbooks (&source_list, NULL)) {
174 g_warning ("ENameSelectorDialog can't find any addressbooks!");
175 g_object_unref (name_selector_dialog->gui);
179 /* Reparent it to inside ourselves */
181 g_object_ref (widget);
182 gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
183 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (name_selector_dialog)->vbox), widget, TRUE, TRUE, 0);
184 g_object_unref (widget);
186 /* Store pointers to relevant widgets */
188 name_selector_dialog->contact_view = GTK_TREE_VIEW (
189 glade_xml_get_widget (name_selector_dialog->gui, "source-tree-view"));
190 name_selector_dialog->status_label = GTK_LABEL (
191 glade_xml_get_widget (name_selector_dialog->gui, "status-message"));
192 name_selector_dialog->destination_box = GTK_BOX (
193 glade_xml_get_widget (name_selector_dialog->gui, "destination-box"));
194 name_selector_dialog->search_entry = GTK_ENTRY (
195 glade_xml_get_widget (name_selector_dialog->gui, "search"));
197 /* Create size group for transfer buttons */
199 name_selector_dialog->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
201 /* Set up contacts view */
203 column = gtk_tree_view_column_new ();
204 cell_renderer = GTK_CELL_RENDERER (gtk_cell_renderer_text_new ());
205 gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
206 gtk_tree_view_column_set_cell_data_func (column, cell_renderer,
207 (GtkTreeCellDataFunc) contact_column_formatter,
208 name_selector_dialog, NULL);
210 selection = gtk_tree_view_get_selection (name_selector_dialog->contact_view);
211 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
212 gtk_tree_view_append_column (name_selector_dialog->contact_view, column);
213 g_signal_connect_swapped (name_selector_dialog->contact_view, "row-activated",
214 G_CALLBACK (contact_activated), name_selector_dialog);
216 /* Listen for changes to the contact selection */
218 contact_selection = gtk_tree_view_get_selection (name_selector_dialog->contact_view);
219 g_signal_connect_swapped (contact_selection, "changed",
220 G_CALLBACK (contact_selection_changed), name_selector_dialog);
222 /* Set up our data structures */
224 name_selector_dialog->name_selector_model = e_name_selector_model_new ();
225 name_selector_dialog->sections = g_array_new (FALSE, FALSE, sizeof (Section));
226 name_selector_dialog->source_list = source_list;
228 setup_name_selector_model (name_selector_dialog);
230 /* Create source menu */
232 widget = e_source_option_menu_new (name_selector_dialog->source_list);
234 gconf_client = gconf_client_get_default();
235 uid = gconf_client_get_string (gconf_client, "/apps/evolution/addressbook/display/primary_addressbook",
237 g_object_unref (gconf_client);
239 ESource *source = e_source_list_peek_source_by_uid(name_selector_dialog->source_list, uid);
241 e_source_option_menu_select ((ESourceOptionMenu *)widget, source);
242 source_selected (name_selector_dialog, source);
245 source_selected (name_selector_dialog, find_first_source (name_selector_dialog->source_list));
250 source_selected (name_selector_dialog, find_first_source (name_selector_dialog->source_list));
253 g_signal_connect_swapped (widget, "source_selected", G_CALLBACK (source_selected), name_selector_dialog);
255 label = glade_xml_get_widget (name_selector_dialog->gui, "AddressBookLabel");
256 gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
258 gtk_widget_show (widget);
260 container = glade_xml_get_widget (name_selector_dialog->gui, "source-menu-box");
261 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
263 e_name_selector_dialog_populate_categories (name_selector_dialog);
265 /* Set up search-as-you-type signal */
267 widget = glade_xml_get_widget (name_selector_dialog->gui, "search");
268 g_signal_connect_swapped (widget, "changed", G_CALLBACK (search_changed), name_selector_dialog);
270 /* Display initial source */
272 /* TODO: Remember last used source */
275 /* Set up dialog defaults */
277 gtk_dialog_add_buttons (GTK_DIALOG (name_selector_dialog),
278 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
281 gtk_dialog_set_default_response (GTK_DIALOG (name_selector_dialog), GTK_RESPONSE_CLOSE);
282 gtk_window_set_modal (GTK_WINDOW (name_selector_dialog), TRUE);
283 gtk_window_set_default_size (GTK_WINDOW (name_selector_dialog), 472, 512);
284 gtk_window_set_resizable (GTK_WINDOW (name_selector_dialog), TRUE);
285 gtk_dialog_set_has_separator (GTK_DIALOG (name_selector_dialog), FALSE);
286 gtk_container_set_border_width (GTK_CONTAINER (name_selector_dialog), 4);
287 gtk_window_set_title (GTK_WINDOW (name_selector_dialog), _("Select Contacts from Address Book"));
288 gtk_widget_grab_focus (widget);
291 /* Partial, repeatable destruction. Release references. */
293 e_name_selector_dialog_dispose (GObject *object)
295 ENameSelectorDialog *name_selector_dialog = E_NAME_SELECTOR_DIALOG (object);
297 remove_books (name_selector_dialog);
298 shutdown_name_selector_model (name_selector_dialog);
300 if (G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->dispose)
301 G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->dispose (object);
304 /* Final, one-time destruction. Free all. */
306 e_name_selector_dialog_finalize (GObject *object)
308 ENameSelectorDialog *name_selector_dialog = E_NAME_SELECTOR_DIALOG (object);
310 g_array_free (name_selector_dialog->sections, TRUE);
311 g_object_unref (name_selector_dialog->source_list);
312 g_object_unref (name_selector_dialog->button_size_group);
313 g_list_free (category_list);
315 if (G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->finalize)
316 G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->finalize (object);
320 e_name_selector_dialog_class_init (ENameSelectorDialogClass *name_selector_dialog_class)
322 GObjectClass *object_class = G_OBJECT_CLASS (name_selector_dialog_class);
324 object_class->get_property = e_name_selector_dialog_get_property;
325 object_class->set_property = e_name_selector_dialog_set_property;
326 object_class->dispose = e_name_selector_dialog_dispose;
327 object_class->finalize = e_name_selector_dialog_finalize;
329 /* Install properties */
331 /* Install signals */
333 g_type_class_add_private (object_class, sizeof(ENameSelectorDialogPrivate));
338 * e_name_selector_dialog_new:
340 * Creates a new #ENameSelectorDialog.
342 * Return value: A new #ENameSelectorDialog.
344 ENameSelectorDialog *
345 e_name_selector_dialog_new (void)
347 return g_object_new (E_TYPE_NAME_SELECTOR_DIALOG, NULL);
355 escape_sexp_string (const gchar *string)
358 gchar *encoded_string;
360 gstring = g_string_new ("");
361 e_sexp_encode_string (gstring, string);
363 encoded_string = gstring->str;
364 g_string_free (gstring, FALSE);
366 return encoded_string;
370 sort_iter_to_contact_store_iter (ENameSelectorDialog *name_selector_dialog, GtkTreeIter *iter,
373 ETreeModelGenerator *contact_filter;
374 GtkTreeIter child_iter;
377 contact_filter = e_name_selector_model_peek_contact_filter (name_selector_dialog->name_selector_model);
379 gtk_tree_model_sort_convert_iter_to_child_iter (name_selector_dialog->contact_sort,
381 e_tree_model_generator_convert_iter_to_child_iter (contact_filter, iter, &email_n_local, &child_iter);
384 *email_n = email_n_local;
388 find_first_source (ESourceList *source_list)
390 GSList *groups, *sources, *l, *m;
392 groups = e_source_list_peek_groups (source_list);
393 for (l = groups; l; l = l->next) {
394 ESourceGroup *group = l->data;
396 sources = e_source_group_peek_sources (group);
397 for (m = sources; m; m = m->next) {
398 ESource *source = m->data;
408 add_destination (EDestinationStore *destination_store, EContact *contact, gint email_n)
410 EDestination *destination;
412 /* Transfer (actually, copy into a destination and let the model filter out the
413 * source automatically) */
415 destination = e_destination_new ();
416 e_destination_set_contact (destination, contact, email_n);
417 e_destination_store_append_destination (destination_store, destination);
418 g_object_unref (destination);
422 remove_books (ENameSelectorDialog *name_selector_dialog)
424 EContactStore *contact_store;
428 if (!name_selector_dialog->name_selector_model)
431 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
433 /* Remove books (should be just one) being viewed */
434 books = e_contact_store_get_books (contact_store);
435 for (l = books; l; l = g_list_next (l)) {
436 EBook *book = l->data;
437 e_contact_store_remove_book (contact_store, book);
441 /* See if we have a book pending; stop loading it if so */
442 if (name_selector_dialog->pending_book) {
443 e_book_cancel (name_selector_dialog->pending_book, NULL);
444 g_object_unref (name_selector_dialog->pending_book);
445 name_selector_dialog->pending_book = NULL;
449 /* ------------------ *
450 * Section management *
451 * ------------------ */
454 find_section_by_transfer_button (ENameSelectorDialog *name_selector_dialog, GtkButton *transfer_button)
458 for (i = 0; i < name_selector_dialog->sections->len; i++) {
459 Section *section = &g_array_index (name_selector_dialog->sections, Section, i);
461 if (section->transfer_button == transfer_button)
469 find_section_by_tree_view (ENameSelectorDialog *name_selector_dialog, GtkTreeView *tree_view)
473 for (i = 0; i < name_selector_dialog->sections->len; i++) {
474 Section *section = &g_array_index (name_selector_dialog->sections, Section, i);
476 if (section->destination_view == tree_view)
484 find_section_by_name (ENameSelectorDialog *name_selector_dialog, const gchar *name)
488 for (i = 0; i < name_selector_dialog->sections->len; i++) {
489 Section *section = &g_array_index (name_selector_dialog->sections, Section, i);
491 if (!strcmp (name, section->name))
499 selection_changed (GtkTreeSelection *selection, SelData *data)
501 GtkTreeSelection *contact_selection;
502 gboolean have_selection = FALSE;
504 contact_selection = gtk_tree_view_get_selection (data->view);
505 if (gtk_tree_selection_count_selected_rows (contact_selection) > 0)
506 have_selection = TRUE;
507 gtk_widget_set_sensitive (GTK_WIDGET (data->button), have_selection);
511 add_section (ENameSelectorDialog *name_selector_dialog,
512 const gchar *name, const gchar *pretty_name, EDestinationStore *destination_store)
515 GtkTreeViewColumn *column;
516 GtkCellRenderer *cell_renderer;
517 GtkWidget *vbox, *hbox, *chbox;
518 GtkWidget *widget, *image, *label;
520 GtkTreeSelection *selection;
523 g_assert (name != NULL);
524 g_assert (pretty_name != NULL);
525 g_assert (E_IS_DESTINATION_STORE (destination_store));
527 memset (§ion, 0, sizeof (Section));
529 section.name = g_strdup (name);
530 section.section_box = GTK_BOX (gtk_hbox_new (FALSE, 12));
531 section.label = GTK_LABEL (gtk_label_new_with_mnemonic (pretty_name));
532 section.transfer_button = GTK_BUTTON (gtk_button_new());
533 section.remove_button = GTK_BUTTON (gtk_button_new());
534 section.destination_view = GTK_TREE_VIEW (gtk_tree_view_new ());
536 gtk_label_set_mnemonic_widget (GTK_LABEL (section.label), GTK_WIDGET (section.destination_view));
538 if (pango_parse_markup (pretty_name, -1, '_', NULL,
539 &text, NULL, NULL)) {
540 atk_object_set_name (gtk_widget_get_accessible (
541 GTK_WIDGET (section.destination_view)), text);
545 /* Set up transfer button */
546 g_signal_connect_swapped (section.transfer_button, "clicked",
547 G_CALLBACK (transfer_button_clicked), name_selector_dialog);
549 /*data for the remove callback*/
550 data = g_malloc0(sizeof(SelData));
551 data->view = section.destination_view;
552 data->dlg_ptr = name_selector_dialog;
554 /*Associate to an object destroy so that it gets freed*/
555 g_object_set_data_full ((GObject *)section.destination_view, "sel-remove-data", data, g_free);
557 g_signal_connect(section.remove_button, "clicked",
558 G_CALLBACK (remove_button_clicked), data);
561 column = gtk_tree_view_column_new ();
562 cell_renderer = GTK_CELL_RENDERER (gtk_cell_renderer_text_new ());
563 gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
564 gtk_tree_view_column_set_cell_data_func (column, cell_renderer,
565 (GtkTreeCellDataFunc) destination_column_formatter,
566 name_selector_dialog, NULL);
567 gtk_tree_view_append_column (section.destination_view, column);
568 gtk_tree_view_set_headers_visible (section.destination_view, FALSE);
569 gtk_tree_view_set_model (section.destination_view, GTK_TREE_MODEL (destination_store));
571 vbox = gtk_vbox_new (FALSE, 0);
572 chbox = gtk_hbox_new (FALSE, 0);
573 gtk_box_pack_start (GTK_BOX (chbox), vbox, FALSE, FALSE, 12);
575 /* Pack button (in an alignment) */
576 widget = gtk_alignment_new (0.5, 0.0, 0.0, 0.0);
577 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (section.transfer_button));
578 gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, TRUE, 6);
579 gtk_size_group_add_widget (name_selector_dialog->button_size_group, GTK_WIDGET (section.transfer_button));
581 /*to get the image embedded in the button*/
582 widget = gtk_alignment_new (0.7, 0.5, 0.0, 0.0);
583 gtk_container_add (GTK_CONTAINER (section.transfer_button), GTK_WIDGET (widget));
585 hbox = gtk_hbox_new (FALSE, 2);
586 gtk_widget_show (GTK_WIDGET(hbox));
587 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET(hbox));
589 label = gtk_label_new_with_mnemonic (_("_Add"));
590 gtk_widget_show (label);
591 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
593 image = gtk_image_new_from_stock ("gtk-go-forward", GTK_ICON_SIZE_BUTTON);
594 gtk_widget_show (image);
595 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
597 widget = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
598 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (section.remove_button));
599 gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, TRUE, 0);
600 gtk_size_group_add_widget (name_selector_dialog->button_size_group, GTK_WIDGET (section.remove_button));
601 gtk_widget_set_sensitive (GTK_WIDGET (section.remove_button), FALSE);
603 widget = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
604 gtk_container_add (GTK_CONTAINER (section.remove_button), widget);
606 hbox = gtk_hbox_new (FALSE, 2);
607 gtk_widget_show (hbox);
608 gtk_container_add (GTK_CONTAINER (widget), hbox);
610 image = gtk_image_new_from_stock ("gtk-go-back", GTK_ICON_SIZE_BUTTON);
611 gtk_widget_show (image);
612 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
614 label = gtk_label_new_with_mnemonic (_("_Remove"));
615 gtk_widget_show (label);
616 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
618 vbox = gtk_vbox_new (FALSE, 0);
620 widget = gtk_alignment_new (0.5, 0.0, 0.0, 0.0);
621 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (section.label));
622 gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, TRUE, 0);
624 /* Pack view (in a scrolled window) */
625 widget = gtk_scrolled_window_new (NULL, NULL);
626 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
627 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
628 gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (section.destination_view));
629 gtk_box_pack_start (GTK_BOX (chbox), widget, TRUE, TRUE, 0);
630 gtk_box_pack_start (GTK_BOX (vbox), chbox, TRUE, TRUE, 0);
631 gtk_box_pack_start (section.section_box, vbox, TRUE, TRUE, 0);
633 /*data for 'changed' callback*/
634 data = g_malloc0(sizeof(SelData));
635 data->view = section.destination_view;
636 data->button = section.remove_button;
637 g_object_set_data_full ((GObject *)section.destination_view, "sel-change-data", data, g_free);
638 selection = gtk_tree_view_get_selection(section.destination_view);
639 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
641 g_signal_connect(selection, "changed",
642 G_CALLBACK (selection_changed), data);
644 g_signal_connect_swapped (section.destination_view, "row-activated",
645 G_CALLBACK (destination_activated), name_selector_dialog);
646 g_signal_connect_swapped (section.destination_view, "key-press-event",
647 G_CALLBACK (destination_key_press), name_selector_dialog);
649 gtk_widget_show_all (GTK_WIDGET (section.section_box));
651 /* Pack this section's box into the dialog */
652 gtk_box_pack_start (name_selector_dialog->destination_box,
653 GTK_WIDGET (section.section_box), TRUE, TRUE, 0);
655 g_array_append_val (name_selector_dialog->sections, section);
657 /* Make sure UI is consistent */
658 contact_selection_changed (name_selector_dialog);
660 return name_selector_dialog->sections->len - 1;
664 free_section (ENameSelectorDialog *name_selector_dialog, gint n)
669 g_assert (n < name_selector_dialog->sections->len);
671 section = &g_array_index (name_selector_dialog->sections, Section, n);
673 g_free (section->name);
674 gtk_widget_destroy (GTK_WIDGET (section->section_box));
678 model_section_added (ENameSelectorDialog *name_selector_dialog, const gchar *name)
681 EDestinationStore *destination_store;
683 e_name_selector_model_peek_section (name_selector_dialog->name_selector_model, name,
684 &pretty_name, &destination_store);
685 add_section (name_selector_dialog, name, pretty_name, destination_store);
686 g_free (pretty_name);
690 model_section_removed (ENameSelectorDialog *name_selector_dialog, const gchar *name)
694 section_index = find_section_by_name (name_selector_dialog, name);
695 g_assert (section_index >= 0);
697 free_section (name_selector_dialog, section_index);
698 g_array_remove_index (name_selector_dialog->sections, section_index);
701 /* -------------------- *
702 * Addressbook selector *
703 * -------------------- */
706 status_message(EBookView *view, const gchar *message, ENameSelectorDialog *dialog)
709 gtk_label_set_text(dialog->status_label, "");
711 gtk_label_set_text(dialog->status_label, message);
715 sequence_complete(EBookView *view, EBookViewStatus status, ENameSelectorDialog *dialog)
717 status_message(view, NULL, dialog);
721 book_opened (EBook *book, EBookStatus status, gpointer data)
723 ENameSelectorDialog *name_selector_dialog = E_NAME_SELECTOR_DIALOG (data);
724 EContactStore *contact_store;
727 if (status != E_BOOK_ERROR_OK) {
728 /* TODO: Handle errors gracefully */
729 gtk_label_set_text(name_selector_dialog->status_label, "Error loading addressbook");
730 g_warning ("ENameSelectorDialog failed to open book!");
734 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
735 e_contact_store_add_book (contact_store, book);
736 view = find_contact_source_by_book_return_view(contact_store, book);
737 g_signal_connect(view, "status_message", G_CALLBACK(status_message), name_selector_dialog);
738 g_signal_connect(view, "sequence_complete", G_CALLBACK(sequence_complete), name_selector_dialog);
740 g_object_unref (book);
741 name_selector_dialog->pending_book = NULL;
745 source_selected (ENameSelectorDialog *name_selector_dialog, ESource *source)
747 /* Remove any previous books being shown or loaded */
748 remove_books (name_selector_dialog);
750 /* Start loading selected book */
751 name_selector_dialog->pending_book = e_load_book_source (source, book_opened,
752 name_selector_dialog);
760 search_changed (ENameSelectorDialog *name_selector_dialog)
762 EContactStore *contact_store;
763 EBookQuery *book_query;
764 GtkWidget *category_option_menu;
769 const gchar *category;
770 gchar *category_escaped;
772 category_option_menu = glade_xml_get_widget(name_selector_dialog->gui, "optionmenu-category");
773 category_id = gtk_option_menu_get_history (GTK_OPTION_MENU(category_option_menu));
774 category = g_list_nth_data (category_list, category_id);
777 category_escaped = escape_sexp_string (category);
779 text = gtk_entry_get_text (name_selector_dialog->search_entry);
780 text_escaped = escape_sexp_string (text);
782 if ( !strcmp (category, _("Any Category"))) {
783 query_string = g_strdup_printf ("(or (beginswith \"file_as\" %s) "
784 " (beginswith \"full_name\" %s) "
785 " (beginswith \"email\" %s) "
786 " (beginswith \"nickname\" %s)))",
787 text_escaped, text_escaped, text_escaped, text_escaped);
790 query_string = g_strdup_printf ("(and (is \"category_list\" %s) "
791 "(or (beginswith \"file_as\" %s) "
792 " (beginswith \"full_name\" %s) "
793 " (beginswith \"email\" %s) "
794 " (beginswith \"nickname\" %s)))",
795 category_escaped,text_escaped, text_escaped, text_escaped, text_escaped);
798 book_query = e_book_query_from_string (query_string);
799 g_free (query_string);
800 g_free (text_escaped);
801 g_free (category_escaped);
803 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
804 e_contact_store_set_query (contact_store, book_query);
806 e_book_query_unref (book_query);
810 contact_selection_changed (ENameSelectorDialog *name_selector_dialog)
812 GtkTreeSelection *contact_selection;
813 gboolean have_selection = FALSE;
816 contact_selection = gtk_tree_view_get_selection (name_selector_dialog->contact_view);
817 if (gtk_tree_selection_count_selected_rows (contact_selection))
818 have_selection = TRUE;
820 for (i = 0; i < name_selector_dialog->sections->len; i++) {
821 Section *section = &g_array_index (name_selector_dialog->sections, Section, i);
822 gtk_widget_set_sensitive (GTK_WIDGET (section->transfer_button), have_selection);
827 contact_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path)
829 EContactStore *contact_store;
830 EDestinationStore *destination_store;
836 ENameSelectorDialogPrivate *priv;
838 /* When a contact is activated, we transfer it to the first destination on our list */
840 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
842 /* If we have no sections, we can't transfer */
843 if (name_selector_dialog->sections->len == 0)
846 /* Get the contact to be transferred */
848 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (name_selector_dialog->contact_sort),
850 g_assert_not_reached ();
852 sort_iter_to_contact_store_iter (name_selector_dialog, &iter, &email_n);
854 contact = e_contact_store_get_contact (contact_store, &iter);
856 g_warning ("ENameSelectorDialog could not get selected contact!");
861 priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (name_selector_dialog);
863 section = &g_array_index (name_selector_dialog->sections, Section, priv->destination_index);
864 if (!e_name_selector_model_peek_section (name_selector_dialog->name_selector_model,
865 section->name, NULL, &destination_store)) {
866 g_warning ("ENameSelectorDialog has a section unknown to the model!");
870 add_destination (destination_store, contact, email_n);
874 destination_activated (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path,
875 GtkTreeViewColumn *column, GtkTreeView *tree_view)
878 EDestinationStore *destination_store;
879 EDestination *destination;
883 /* When a destination is activated, we remove it from the section */
885 section_index = find_section_by_tree_view (name_selector_dialog, tree_view);
886 if (section_index < 0) {
887 g_warning ("ENameSelectorDialog got activation from unknown view!");
891 section = &g_array_index (name_selector_dialog->sections, Section, section_index);
892 if (!e_name_selector_model_peek_section (name_selector_dialog->name_selector_model,
893 section->name, NULL, &destination_store)) {
894 g_warning ("ENameSelectorDialog has a section unknown to the model!");
898 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store), &iter, path))
899 g_assert_not_reached ();
901 destination = e_destination_store_get_destination (destination_store, &iter);
902 g_assert (destination);
904 e_destination_store_remove_destination (destination_store, destination);
908 remove_selection (ENameSelectorDialog *name_selector_dialog, GtkTreeView *tree_view)
911 EDestinationStore *destination_store;
912 EDestination *destination;
914 GtkTreeSelection *selection;
917 section_index = find_section_by_tree_view (name_selector_dialog, tree_view);
918 if (section_index < 0) {
919 g_warning ("ENameSelectorDialog got key press from unknown view!");
923 section = &g_array_index (name_selector_dialog->sections, Section, section_index);
924 if (!e_name_selector_model_peek_section (name_selector_dialog->name_selector_model,
925 section->name, NULL, &destination_store)) {
926 g_warning ("ENameSelectorDialog has a section unknown to the model!");
930 selection = gtk_tree_view_get_selection (tree_view);
931 if (!gtk_tree_selection_count_selected_rows (selection)) {
932 g_warning ("ENameSelectorDialog remove button clicked, but no selection!");
936 rows = gtk_tree_selection_get_selected_rows (selection, NULL);
937 rows = g_list_reverse (rows);
939 for (l = rows; l; l = g_list_next(l)) {
941 GtkTreePath *path = l->data;
943 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store),
945 g_assert_not_reached ();
947 gtk_tree_path_free (path);
949 destination = e_destination_store_get_destination (destination_store, &iter);
950 g_assert (destination);
952 e_destination_store_remove_destination (destination_store, destination);
960 remove_button_clicked (GtkButton *button, SelData *data)
963 ENameSelectorDialog *name_selector_dialog;
966 name_selector_dialog = data->dlg_ptr;
967 remove_selection (name_selector_dialog, view);
971 destination_key_press (ENameSelectorDialog *name_selector_dialog,
972 GdkEventKey *event, GtkTreeView *tree_view)
975 /* we only care about DEL key */
976 if (event->keyval != GDK_Delete)
978 return remove_selection (name_selector_dialog, tree_view);
983 transfer_button_clicked (ENameSelectorDialog *name_selector_dialog, GtkButton *transfer_button)
985 EContactStore *contact_store;
986 EDestinationStore *destination_store;
987 GtkTreeSelection *selection;
994 /* Get the contact to be transferred */
996 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
997 selection = gtk_tree_view_get_selection (name_selector_dialog->contact_view);
999 if (!gtk_tree_selection_count_selected_rows (selection)) {
1000 g_warning ("ENameSelectorDialog transfer button clicked, but no selection!");
1004 /* Get the target section */
1005 section_index = find_section_by_transfer_button (name_selector_dialog, transfer_button);
1006 if (section_index < 0) {
1007 g_warning ("ENameSelectorDialog got click from unknown button!");
1011 section = &g_array_index (name_selector_dialog->sections, Section, section_index);
1012 if (!e_name_selector_model_peek_section (name_selector_dialog->name_selector_model,
1013 section->name, NULL, &destination_store)) {
1014 g_warning ("ENameSelectorDialog has a section unknown to the model!");
1018 rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1019 rows = g_list_reverse (rows);
1021 for (l = rows; l; l = g_list_next(l)) {
1023 GtkTreePath *path = l->data;
1025 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (name_selector_dialog->contact_sort),
1027 gtk_tree_path_free (path);
1031 gtk_tree_path_free (path);
1032 sort_iter_to_contact_store_iter (name_selector_dialog, &iter, &email_n);
1034 contact = e_contact_store_get_contact (contact_store, &iter);
1036 g_warning ("ENameSelectorDialog could not get selected contact!");
1041 add_destination (destination_store, contact, email_n);
1046 /* --------------------- *
1047 * Main model management *
1048 * --------------------- */
1051 setup_name_selector_model (ENameSelectorDialog *name_selector_dialog)
1053 EContactStore *contact_store;
1054 ETreeModelGenerator *contact_filter;
1055 GList *new_sections;
1058 /* Create new destination sections in UI */
1060 new_sections = e_name_selector_model_list_sections (name_selector_dialog->name_selector_model);
1062 for (l = new_sections; l; l = g_list_next (l)) {
1063 gchar *name = l->data;
1065 EDestinationStore *destination_store;
1067 e_name_selector_model_peek_section (name_selector_dialog->name_selector_model,
1068 name, &pretty_name, &destination_store);
1070 add_section (name_selector_dialog, name, pretty_name, destination_store);
1072 g_free (pretty_name);
1076 g_list_free (new_sections);
1078 /* Connect to section add/remove signals */
1080 g_signal_connect_swapped (name_selector_dialog->name_selector_model, "section-added",
1081 G_CALLBACK (model_section_added), name_selector_dialog);
1082 g_signal_connect_swapped (name_selector_dialog->name_selector_model, "section-removed",
1083 G_CALLBACK (model_section_removed), name_selector_dialog);
1085 /* Get contact store and its filter wrapper */
1087 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
1088 contact_filter = e_name_selector_model_peek_contact_filter (name_selector_dialog->name_selector_model);
1090 /* Create sorting model on top of filter, assign it to view */
1092 name_selector_dialog->contact_sort = GTK_TREE_MODEL_SORT (
1093 gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (contact_filter)));
1095 /* sort on full name as we display full name in name selector dialog */
1096 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (name_selector_dialog->contact_sort),
1097 E_CONTACT_FULL_NAME, GTK_SORT_ASCENDING);
1099 gtk_tree_view_set_model (name_selector_dialog->contact_view,
1100 GTK_TREE_MODEL (name_selector_dialog->contact_sort));
1102 /* Make sure UI is consistent */
1104 search_changed (name_selector_dialog);
1105 contact_selection_changed (name_selector_dialog);
1109 shutdown_name_selector_model (ENameSelectorDialog *name_selector_dialog)
1113 /* Rid UI of previous destination sections */
1115 for (i = 0; i < name_selector_dialog->sections->len; i++)
1116 free_section (name_selector_dialog, i);
1118 g_array_set_size (name_selector_dialog->sections, 0);
1120 /* Free sorting model */
1122 if (name_selector_dialog->contact_sort) {
1123 g_object_unref (name_selector_dialog->contact_sort);
1124 name_selector_dialog->contact_sort = NULL;
1127 /* Free backend model */
1129 if (name_selector_dialog->name_selector_model) {
1130 g_signal_handlers_disconnect_matched (name_selector_dialog->name_selector_model,
1131 G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_dialog);
1133 g_object_unref (name_selector_dialog->name_selector_model);
1134 name_selector_dialog->name_selector_model = NULL;
1139 deep_free_list (GList *list)
1143 for (l = list; l; l = g_list_next (l))
1150 contact_column_formatter (GtkTreeViewColumn *column, GtkCellRenderer *cell, GtkTreeModel *model,
1151 GtkTreeIter *iter, ENameSelectorDialog *name_selector_dialog)
1153 EContactStore *contact_store;
1155 GtkTreeIter contact_store_iter;
1158 gchar *full_name_str;
1162 contact_store_iter = *iter;
1163 sort_iter_to_contact_store_iter (name_selector_dialog, &contact_store_iter, &email_n);
1165 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->name_selector_model);
1166 contact = e_contact_store_get_contact (contact_store, &contact_store_iter);
1167 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
1168 email_str = g_list_nth_data (email_list, email_n);
1169 full_name_str = e_contact_get (contact, E_CONTACT_FULL_NAME);
1171 if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
1172 string = g_strdup_printf ("%s", full_name_str ? full_name_str : "?");
1174 string = g_strdup_printf ("%s%s<%s>", full_name_str ? full_name_str : "",
1175 full_name_str ? " " : "",
1176 email_str ? email_str : "");
1179 g_free (full_name_str);
1180 deep_free_list (email_list);
1182 g_object_set (cell, "text", string, NULL);
1187 destination_column_formatter (GtkTreeViewColumn *column, GtkCellRenderer *cell, GtkTreeModel *model,
1188 GtkTreeIter *iter, ENameSelectorDialog *name_selector_dialog)
1190 EDestinationStore *destination_store = E_DESTINATION_STORE (model);
1191 EDestination *destination;
1194 destination = e_destination_store_get_destination (destination_store, iter);
1195 g_assert (destination);
1197 buffer = g_string_new (e_destination_get_name (destination));
1199 if (!e_destination_is_evolution_list (destination)) {
1202 email = e_destination_get_email (destination);
1203 if (email == NULL || *email == '\0')
1205 g_string_append_printf (buffer, " <%s>", email);
1208 g_object_set (cell, "text", buffer->str, NULL);
1209 g_string_free (buffer, TRUE);
1212 /* ----------------------- *
1213 * ENameSelectorDialog API *
1214 * ----------------------- */
1217 * e_name_selector_dialog_peek_model:
1218 * @name_selector_dialog: an #ENameSelectorDialog
1220 * Gets the #ENameSelectorModel used by @name_selector_model.
1222 * Return value: The #ENameSelectorModel being used.
1224 ENameSelectorModel *
1225 e_name_selector_dialog_peek_model (ENameSelectorDialog *name_selector_dialog)
1227 g_return_val_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog), NULL);
1229 return name_selector_dialog->name_selector_model;
1233 * e_name_selector_dialog_set_model:
1234 * @name_selector_dialog: an #ENameSelectorDialog
1235 * @model: an #ENameSelectorModel
1237 * Sets the model being used by @name_selector_dialog to @model.
1240 e_name_selector_dialog_set_model (ENameSelectorDialog *name_selector_dialog,
1241 ENameSelectorModel *model)
1243 g_return_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog));
1244 g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (model));
1246 if (model == name_selector_dialog->name_selector_model)
1249 shutdown_name_selector_model (name_selector_dialog);
1250 name_selector_dialog->name_selector_model = g_object_ref (model);
1252 setup_name_selector_model (name_selector_dialog);
1256 * e_name_selector_dialog_set_destination_index:
1257 * @name_selector_dialog: an #ENameSelectorDialog
1258 * @index: index of the destination section, starting from 0.
1260 * Sets the index number of the destination section.
1263 e_name_selector_dialog_set_destination_index (ENameSelectorDialog *name_selector_dialog,
1266 ENameSelectorDialogPrivate *priv;
1268 priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (name_selector_dialog);
1270 if (index >= name_selector_dialog->sections->len)
1273 priv->destination_index = index;
1276 /* ----------------------------------- *
1277 * Widget creation functions for Glade *
1278 * ----------------------------------- */
1280 #ifdef CATEGORIES_COMPONENTS_MOVED
1283 e_name_selector_dialog_create_categories (void)
1285 ECategoriesMasterList *ecml;
1286 GtkWidget *option_menu;
1288 ecml = e_categories_master_list_wombat_new ();
1289 option_menu = e_categories_master_list_option_menu_new (ecml);
1290 g_object_unref (ecml);