Assamese translation updated
[platform/upstream/evolution-data-server.git] / libedataserverui / e-name-selector-dialog.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-name-selector-dialog.c - Dialog that lets user pick EDestinations.
4  *
5  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
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.
10  *
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.
15  *
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.
20  *
21  * Author: Hans Petter Jansson <hpj@novell.com>
22  */
23
24 #ifdef GTK_DISABLE_DEPRECATED
25 #undef GTK_DISABLE_DEPRECATED
26 #endif
27
28 #include <config.h>
29 #include <string.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <glib/gi18n-lib.h>
32
33 #include <libebook/libebook.h>
34 #include <libebackend/libebackend.h>
35
36 #include "e-source-combo-box.h"
37 #include "e-destination-store.h"
38 #include "e-contact-store.h"
39 #include "e-client-utils.h"
40 #include "e-name-selector-dialog.h"
41 #include "e-name-selector-entry.h"
42
43 #define E_NAME_SELECTOR_DIALOG_GET_PRIVATE(obj) \
44         (G_TYPE_INSTANCE_GET_PRIVATE \
45         ((obj), E_TYPE_NAME_SELECTOR_DIALOG, ENameSelectorDialogPrivate))
46
47 typedef struct {
48         gchar        *name;
49
50         GtkGrid      *section_grid;
51         GtkLabel     *label;
52         GtkButton    *transfer_button;
53         GtkButton    *remove_button;
54         GtkTreeView  *destination_view;
55 }
56 Section;
57
58 typedef struct {
59         GtkTreeView *view;
60         GtkButton   *button;
61         ENameSelectorDialog *dlg_ptr;
62 } SelData;
63
64 struct _ENameSelectorDialogPrivate {
65         ESourceRegistry *registry;
66         ENameSelectorModel *name_selector_model;
67         GtkTreeModelSort *contact_sort;
68         GCancellable *cancellable;
69
70         GtkTreeView *contact_view;
71         GtkLabel *status_label;
72         GtkGrid *destination_vgrid;
73         GtkEntry *search_entry;
74         GtkSizeGroup *button_size_group;
75         GtkWidget *category_combobox;
76         GtkWidget *contact_window;
77
78         GArray *sections;
79
80         guint destination_index;
81         GSList *user_query_fields;
82         GtkSizeGroup *dest_label_size_group;
83 };
84
85 enum {
86         PROP_0,
87         PROP_REGISTRY
88 };
89
90 static void     search_changed                (ENameSelectorDialog *name_selector_dialog);
91 static void     source_changed                (ENameSelectorDialog *name_selector_dialog, ESourceComboBox *source_combo_box);
92 static void     transfer_button_clicked       (ENameSelectorDialog *name_selector_dialog, GtkButton *transfer_button);
93 static void     contact_selection_changed     (ENameSelectorDialog *name_selector_dialog);
94 static void     setup_name_selector_model     (ENameSelectorDialog *name_selector_dialog);
95 static void     shutdown_name_selector_model  (ENameSelectorDialog *name_selector_dialog);
96 static void     contact_activated             (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path);
97 static void     destination_activated         (ENameSelectorDialog *name_selector_dialog, GtkTreePath *path,
98                                                GtkTreeViewColumn *column, GtkTreeView *tree_view);
99 static gboolean destination_key_press         (ENameSelectorDialog *name_selector_dialog, GdkEventKey *event, GtkTreeView *tree_view);
100 static void remove_button_clicked (GtkButton *button, SelData *data);
101 static void     remove_books                  (ENameSelectorDialog *name_selector_dialog);
102 static void     contact_column_formatter      (GtkTreeViewColumn *column, GtkCellRenderer *cell,
103                                                GtkTreeModel *model, GtkTreeIter *iter,
104                                                ENameSelectorDialog *name_selector_dialog);
105 static void     destination_column_formatter  (GtkTreeViewColumn *column, GtkCellRenderer *cell,
106                                                GtkTreeModel *model, GtkTreeIter *iter,
107                                                ENameSelectorDialog *name_selector_dialog);
108
109 /* ------------------ *
110  * Class/object setup *
111  * ------------------ */
112
113 G_DEFINE_TYPE_WITH_CODE (
114         ENameSelectorDialog,
115         e_name_selector_dialog,
116         GTK_TYPE_DIALOG,
117         G_IMPLEMENT_INTERFACE (
118                 E_TYPE_EXTENSIBLE, NULL))
119
120 static void
121 name_selector_dialog_populate_categories (ENameSelectorDialog *name_selector_dialog)
122 {
123         GtkWidget *combo_box;
124         GList *category_list, *iter;
125
126         /* "Any Category" is preloaded. */
127         combo_box = name_selector_dialog->priv->category_combobox;
128         if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) == -1)
129                 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
130
131         /* Categories are already sorted. */
132         category_list = e_categories_get_list ();
133         for (iter = category_list; iter != NULL; iter = iter->next) {
134                 /* Only add user-visible categories. */
135                 if (!e_categories_is_searchable (iter->data))
136                         continue;
137
138                 gtk_combo_box_text_append_text (
139                         GTK_COMBO_BOX_TEXT (combo_box), iter->data);
140         }
141
142         g_list_free (category_list);
143
144         g_signal_connect_swapped (
145                 combo_box, "changed",
146                 G_CALLBACK (search_changed), name_selector_dialog);
147 }
148
149 static void
150 name_selector_dialog_set_registry (ENameSelectorDialog *name_selector_dialog,
151                                    ESourceRegistry *registry)
152 {
153         g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
154         g_return_if_fail (name_selector_dialog->priv->registry == NULL);
155
156         name_selector_dialog->priv->registry = g_object_ref (registry);
157 }
158
159 static void
160 name_selector_dialog_set_property (GObject *object,
161                                    guint property_id,
162                                    const GValue *value,
163                                    GParamSpec *pspec)
164 {
165         switch (property_id) {
166                 case PROP_REGISTRY:
167                         name_selector_dialog_set_registry (
168                                 E_NAME_SELECTOR_DIALOG (object),
169                                 g_value_get_object (value));
170                         return;
171         }
172
173         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
174 }
175
176 static void
177 name_selector_dialog_get_property (GObject *object,
178                                    guint property_id,
179                                    GValue *value,
180                                    GParamSpec *pspec)
181 {
182         switch (property_id) {
183                 case PROP_REGISTRY:
184                         g_value_set_object (
185                                 value,
186                                 e_name_selector_dialog_get_registry (
187                                 E_NAME_SELECTOR_DIALOG (object)));
188                         return;
189         }
190
191         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
192 }
193
194 static void
195 name_selector_dialog_dispose (GObject *object)
196 {
197         ENameSelectorDialogPrivate *priv;
198
199         priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (object);
200
201         remove_books (E_NAME_SELECTOR_DIALOG (object));
202         shutdown_name_selector_model (E_NAME_SELECTOR_DIALOG (object));
203
204         if (priv->registry != NULL) {
205                 g_object_unref (priv->registry);
206                 priv->registry = NULL;
207         }
208
209         /* Chain up to parent's dispose() method. */
210         G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->dispose (object);
211 }
212
213 static void
214 name_selector_dialog_finalize (GObject *object)
215 {
216         ENameSelectorDialogPrivate *priv;
217
218         priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (object);
219
220         g_slist_foreach (priv->user_query_fields, (GFunc) g_free, NULL);
221         g_slist_free (priv->user_query_fields);
222
223         g_array_free (priv->sections, TRUE);
224         g_object_unref (priv->button_size_group);
225         g_object_unref (priv->dest_label_size_group);
226
227         /* Chain up to parent's finalize() method. */
228         G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->finalize (object);
229 }
230
231 static void
232 name_selector_dialog_constructed (GObject *object)
233 {
234         ENameSelectorDialogPrivate *priv;
235         GtkTreeSelection  *contact_selection;
236         GtkTreeViewColumn *column;
237         GtkCellRenderer   *cell_renderer;
238         GtkTreeSelection  *selection;
239         ESource *source;
240         gchar *tmp_str;
241         GtkWidget *name_selector_grid;
242         GtkWidget *show_contacts_label;
243         GtkWidget *hgrid;
244         GtkWidget *label;
245         GtkWidget *show_contacts_grid;
246         GtkWidget *AddressBookLabel;
247         GtkWidget *label_category;
248         GtkWidget *search;
249         AtkObject *atko;
250         GtkWidget *label_search;
251         GtkWidget *source_menu_hgrid;
252         GtkWidget *combobox_category;
253         GtkWidget *label_contacts;
254         GtkWidget *scrolledwindow0;
255         GtkWidget *scrolledwindow1;
256         AtkRelationSet *tmp_relation_set;
257         AtkRelationType tmp_relationship;
258         AtkRelation *tmp_relation;
259         AtkObject *scrolledwindow1_relation_targets[1];
260         GtkWidget *source_tree_view;
261         GtkWidget *destination_vgrid;
262         GtkWidget *status_message;
263         GtkWidget *source_combo;
264         const gchar *extension_name;
265
266         priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (object);
267
268         /* Chain up to parent's constructed() method. */
269         G_OBJECT_CLASS (e_name_selector_dialog_parent_class)->constructed (object);
270
271         name_selector_grid = g_object_new (GTK_TYPE_GRID,
272                 "orientation", GTK_ORIENTATION_VERTICAL,
273                 "column-homogeneous", FALSE,
274                 "row-spacing", 6,
275                 NULL);
276         gtk_widget_show (name_selector_grid);
277         gtk_container_set_border_width (GTK_CONTAINER (name_selector_grid), 0);
278
279         tmp_str = g_strconcat ("<b>", _("Show Contacts"), "</b>", NULL);
280         show_contacts_label = gtk_label_new (tmp_str);
281         gtk_widget_show (show_contacts_label);
282         gtk_container_add (GTK_CONTAINER (name_selector_grid), show_contacts_label);
283         gtk_label_set_use_markup (GTK_LABEL (show_contacts_label), TRUE);
284         gtk_misc_set_alignment (GTK_MISC (show_contacts_label), 0, 0.5);
285         g_free (tmp_str);
286
287         hgrid = g_object_new (GTK_TYPE_GRID,
288                 "orientation", GTK_ORIENTATION_HORIZONTAL,
289                 "row-homogeneous", FALSE,
290                 "column-spacing", 12,
291                 NULL);
292         gtk_widget_show (hgrid);
293         gtk_container_add (GTK_CONTAINER (name_selector_grid), hgrid);
294
295         label = gtk_label_new ("");
296         gtk_widget_show (label);
297         gtk_container_add (GTK_CONTAINER (hgrid), label);
298
299         show_contacts_grid = gtk_grid_new ();
300         gtk_widget_show (show_contacts_grid);
301         gtk_container_add (GTK_CONTAINER (hgrid), show_contacts_grid);
302         g_object_set (G_OBJECT (show_contacts_grid),
303                 "column-spacing", 12,
304                 "row-spacing", 6,
305                 "hexpand", TRUE,
306                 "halign", GTK_ALIGN_FILL,
307                 NULL);
308
309         AddressBookLabel = gtk_label_new_with_mnemonic (_("Address B_ook:"));
310         gtk_widget_show (AddressBookLabel);
311         gtk_grid_attach (GTK_GRID (show_contacts_grid), AddressBookLabel, 0, 0, 1, 1);
312         gtk_widget_set_halign (AddressBookLabel, GTK_ALIGN_FILL);
313         gtk_label_set_justify (GTK_LABEL (AddressBookLabel), GTK_JUSTIFY_CENTER);
314         gtk_misc_set_alignment (GTK_MISC (AddressBookLabel), 0, 0.5);
315
316         label_category = gtk_label_new_with_mnemonic (_("Cat_egory:"));
317         gtk_widget_show (label_category);
318         gtk_grid_attach (GTK_GRID (show_contacts_grid), label_category, 0, 1, 1, 1);
319         gtk_widget_set_halign (label_category, GTK_ALIGN_FILL);
320         gtk_label_set_justify (GTK_LABEL (label_category), GTK_JUSTIFY_CENTER);
321         gtk_misc_set_alignment (GTK_MISC (label_category), 0, 0.5);
322
323         hgrid = g_object_new (GTK_TYPE_GRID,
324                 "orientation", GTK_ORIENTATION_HORIZONTAL,
325                 "row-homogeneous", FALSE,
326                 "column-spacing", 12,
327                 "hexpand", TRUE,
328                 "halign", GTK_ALIGN_FILL,
329                 NULL);
330         gtk_widget_show (hgrid);
331         gtk_grid_attach (GTK_GRID (show_contacts_grid), hgrid, 1, 2, 1, 1);
332
333         search = gtk_entry_new ();
334         gtk_widget_show (search);
335         gtk_widget_set_hexpand (search, TRUE);
336         gtk_widget_set_halign (search, GTK_ALIGN_FILL);
337         gtk_container_add (GTK_CONTAINER (hgrid), search);
338
339         label_search = gtk_label_new_with_mnemonic (_("_Search:"));
340         gtk_widget_show (label_search);
341         gtk_grid_attach (GTK_GRID (show_contacts_grid), label_search, 0, 2, 1, 1);
342         gtk_widget_set_halign (label_search, GTK_ALIGN_FILL);
343         gtk_misc_set_alignment (GTK_MISC (label_search), 0, 0.5);
344
345         source_menu_hgrid = g_object_new (GTK_TYPE_GRID,
346                 "orientation", GTK_ORIENTATION_HORIZONTAL,
347                 "row-homogeneous", FALSE,
348                 "column-spacing", 0,
349                 "halign", GTK_ALIGN_FILL,
350                 "valign", GTK_ALIGN_FILL,
351                 NULL);
352         gtk_widget_show (source_menu_hgrid);
353         gtk_grid_attach (GTK_GRID (show_contacts_grid), source_menu_hgrid, 1, 0, 1, 1);
354
355         combobox_category = gtk_combo_box_text_new ();
356         gtk_widget_show (combobox_category);
357         g_object_set (G_OBJECT (combobox_category),
358                 "halign", GTK_ALIGN_FILL,
359                 "valign", GTK_ALIGN_FILL,
360                 NULL);
361         gtk_grid_attach (GTK_GRID (show_contacts_grid), combobox_category, 1, 1, 1, 1);
362         gtk_combo_box_text_append_text (
363                 GTK_COMBO_BOX_TEXT (combobox_category), _("Any Category"));
364
365         tmp_str = g_strconcat ("<b>", _("Co_ntacts"), "</b>", NULL);
366         label_contacts = gtk_label_new_with_mnemonic (tmp_str);
367         gtk_widget_show (label_contacts);
368         gtk_container_add (GTK_CONTAINER (name_selector_grid), label_contacts);
369         gtk_label_set_use_markup (GTK_LABEL (label_contacts), TRUE);
370         gtk_misc_set_alignment (GTK_MISC (label_contacts), 0, 0.5);
371         g_free (tmp_str);
372
373         scrolledwindow0 = gtk_scrolled_window_new (NULL, NULL);
374         priv->contact_window = scrolledwindow0;
375         gtk_widget_show (scrolledwindow0);
376         gtk_widget_set_vexpand (scrolledwindow0, TRUE);
377         gtk_widget_set_valign (scrolledwindow0, GTK_ALIGN_FILL);
378         gtk_container_add (GTK_CONTAINER (name_selector_grid), scrolledwindow0);
379         gtk_scrolled_window_set_policy (
380                 GTK_SCROLLED_WINDOW (scrolledwindow0),
381                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
382
383         hgrid = g_object_new (GTK_TYPE_GRID,
384                 "orientation", GTK_ORIENTATION_HORIZONTAL,
385                 "row-homogeneous", FALSE,
386                 "column-spacing", 12,
387                 NULL);
388         gtk_widget_show (hgrid);
389         gtk_scrolled_window_add_with_viewport (
390                 GTK_SCROLLED_WINDOW (scrolledwindow0), hgrid);
391
392         label = gtk_label_new ("");
393         gtk_widget_show (label);
394         gtk_container_add (GTK_CONTAINER (hgrid), label);
395
396         scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
397         gtk_widget_show (scrolledwindow1);
398         gtk_container_add (GTK_CONTAINER (hgrid), scrolledwindow1);
399         gtk_widget_set_hexpand (scrolledwindow1, TRUE);
400         gtk_widget_set_halign (scrolledwindow1, GTK_ALIGN_FILL);
401         gtk_scrolled_window_set_policy (
402                 GTK_SCROLLED_WINDOW (scrolledwindow1),
403                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
404         gtk_scrolled_window_set_shadow_type (
405                 GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_SHADOW_IN);
406
407         source_tree_view = gtk_tree_view_new ();
408         gtk_widget_show (source_tree_view);
409         gtk_container_add (GTK_CONTAINER (scrolledwindow1), source_tree_view);
410         gtk_tree_view_set_headers_visible (
411                 GTK_TREE_VIEW (source_tree_view), FALSE);
412         gtk_tree_view_set_enable_search (
413                 GTK_TREE_VIEW (source_tree_view), FALSE);
414
415         destination_vgrid = g_object_new (GTK_TYPE_GRID,
416                 "orientation", GTK_ORIENTATION_VERTICAL,
417                 "column-homogeneous", TRUE,
418                 "row-spacing", 6,
419                 "hexpand", TRUE,
420                 "halign", GTK_ALIGN_FILL,
421                 "vexpand", TRUE,
422                 "valign", GTK_ALIGN_FILL,
423                 NULL);
424         gtk_widget_show (destination_vgrid);
425         gtk_container_add (GTK_CONTAINER (hgrid), destination_vgrid);
426
427         status_message = gtk_label_new ("");
428         gtk_widget_show (status_message);
429         gtk_container_add (GTK_CONTAINER (name_selector_grid), status_message);
430         gtk_label_set_use_markup (GTK_LABEL (status_message), TRUE);
431         gtk_misc_set_alignment (GTK_MISC (status_message), 0, 0.5);
432         gtk_misc_set_padding (GTK_MISC (status_message), 0, 3);
433
434         gtk_label_set_mnemonic_widget (GTK_LABEL (AddressBookLabel), source_menu_hgrid);
435         gtk_label_set_mnemonic_widget (GTK_LABEL (label_category), combobox_category);
436         gtk_label_set_mnemonic_widget (GTK_LABEL (label_search), search);
437         gtk_label_set_mnemonic_widget (GTK_LABEL (label_contacts), source_tree_view);
438
439         atko = gtk_widget_get_accessible (search);
440         atk_object_set_name (atko, _("Search"));
441
442         atko = gtk_widget_get_accessible (source_menu_hgrid);
443         atk_object_set_name (atko, _("Address Book"));
444
445         atko = gtk_widget_get_accessible (scrolledwindow1);
446         atk_object_set_name (atko, _("Contacts"));
447         tmp_relation_set = atk_object_ref_relation_set (atko);
448         scrolledwindow1_relation_targets[0] = gtk_widget_get_accessible (label_contacts);
449         tmp_relationship = atk_relation_type_for_name ("labelled-by");
450         tmp_relation = atk_relation_new (scrolledwindow1_relation_targets, 1, tmp_relationship);
451         atk_relation_set_add (tmp_relation_set, tmp_relation);
452         g_object_unref (G_OBJECT (tmp_relation));
453         g_object_unref (G_OBJECT (tmp_relation_set));
454
455         gtk_box_pack_start (
456                 GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (object))),
457                 name_selector_grid, TRUE, TRUE, 0);
458
459         /* Store pointers to relevant widgets */
460
461         priv->contact_view = GTK_TREE_VIEW (source_tree_view);
462         priv->status_label = GTK_LABEL (status_message);
463         priv->destination_vgrid = GTK_GRID (destination_vgrid);
464         priv->search_entry = GTK_ENTRY (search);
465         priv->category_combobox = combobox_category;
466
467         /* Create size group for transfer buttons */
468
469         priv->button_size_group =
470                 gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
471
472         /* Create size group for destination labels */
473
474         priv->dest_label_size_group =
475                 gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
476
477         /* Set up contacts view */
478
479         column = gtk_tree_view_column_new ();
480         cell_renderer = GTK_CELL_RENDERER (gtk_cell_renderer_text_new ());
481         gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
482         gtk_tree_view_column_set_cell_data_func (
483                 column, cell_renderer, (GtkTreeCellDataFunc)
484                 contact_column_formatter, object, NULL);
485
486         selection = gtk_tree_view_get_selection (priv->contact_view);
487         gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
488         gtk_tree_view_append_column (priv->contact_view, column);
489         g_signal_connect_swapped (
490                 priv->contact_view, "row-activated",
491                 G_CALLBACK (contact_activated), object);
492
493         /* Listen for changes to the contact selection */
494
495         contact_selection = gtk_tree_view_get_selection (priv->contact_view);
496         g_signal_connect_swapped (
497                 contact_selection, "changed",
498                 G_CALLBACK (contact_selection_changed), object);
499
500         /* Set up our data structures */
501
502         priv->name_selector_model = e_name_selector_model_new ();
503         priv->sections = g_array_new (FALSE, FALSE, sizeof (Section));
504
505         setup_name_selector_model (E_NAME_SELECTOR_DIALOG (object));
506
507         /* Create source menu */
508
509         extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
510         source_combo = e_source_combo_box_new (priv->registry, extension_name);
511         g_signal_connect_swapped (
512                 source_combo, "changed",
513                 G_CALLBACK (source_changed), object);
514
515         source_changed (E_NAME_SELECTOR_DIALOG (object), E_SOURCE_COMBO_BOX (source_combo));
516
517         gtk_label_set_mnemonic_widget (GTK_LABEL (AddressBookLabel), source_combo);
518         gtk_widget_show (source_combo);
519         gtk_widget_set_hexpand (source_combo, TRUE);
520         gtk_widget_set_halign (source_combo, GTK_ALIGN_FILL);
521         gtk_container_add (GTK_CONTAINER (source_menu_hgrid), source_combo);
522
523         name_selector_dialog_populate_categories (
524                 E_NAME_SELECTOR_DIALOG (object));
525
526         /* Set up search-as-you-type signal */
527
528         g_signal_connect_swapped (
529                 search, "changed",
530                 G_CALLBACK (search_changed), object);
531
532         /* Display initial source */
533
534         source = e_source_registry_ref_default_address_book (priv->registry);
535         e_source_combo_box_set_active (
536                 E_SOURCE_COMBO_BOX (source_combo), source);
537         g_object_unref (source);
538
539         /* Set up dialog defaults */
540
541         gtk_dialog_add_buttons (
542                 GTK_DIALOG (object),
543                 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
544                 NULL);
545
546         /* Try to figure out a sane default size for the dialog. We used to hard
547          * code this to 512 so keep using 512 if the screen is big enough,
548          * otherwise use -1 (use as little as possible, use the
549          * GtkScrolledWindow's scrollbars).
550          *
551          * This should allow scrolling on tiny netbook resolutions and let
552          * others see as much of the dialog as possible.
553          *
554          * 600 pixels seems to be a good lower bound resolution to allow room
555          * above or below for other UI (window manager's?)
556          */
557         gtk_window_set_default_size (
558                 GTK_WINDOW (object), 700,
559                 gdk_screen_height () >= 600 ? 512 : -1);
560
561         gtk_dialog_set_default_response (
562                 GTK_DIALOG (object), GTK_RESPONSE_CLOSE);
563         gtk_window_set_modal (GTK_WINDOW (object), TRUE);
564         gtk_window_set_resizable (GTK_WINDOW (object), TRUE);
565         gtk_container_set_border_width (GTK_CONTAINER (object), 4);
566         gtk_window_set_title (
567                 GTK_WINDOW (object),
568                 _("Select Contacts from Address Book"));
569         gtk_widget_grab_focus (search);
570
571         e_extensible_load_extensions (E_EXTENSIBLE (object));
572 }
573
574 static void
575 e_name_selector_dialog_class_init (ENameSelectorDialogClass *class)
576 {
577         GObjectClass *object_class;
578
579         g_type_class_add_private (class, sizeof (ENameSelectorDialogPrivate));
580
581         object_class = G_OBJECT_CLASS (class);
582         object_class->set_property = name_selector_dialog_set_property;
583         object_class->get_property = name_selector_dialog_get_property;
584         object_class->dispose = name_selector_dialog_dispose;
585         object_class->finalize = name_selector_dialog_finalize;
586         object_class->constructed = name_selector_dialog_constructed;
587
588         g_object_class_install_property (
589                 object_class,
590                 PROP_REGISTRY,
591                 g_param_spec_object (
592                         "registry",
593                         "Registry",
594                         "Data source registry",
595                         E_TYPE_SOURCE_REGISTRY,
596                         G_PARAM_READWRITE |
597                         G_PARAM_CONSTRUCT_ONLY |
598                         G_PARAM_STATIC_STRINGS));
599 }
600
601 static void
602 e_name_selector_dialog_init (ENameSelectorDialog *name_selector_dialog)
603 {
604         name_selector_dialog->priv =
605                 E_NAME_SELECTOR_DIALOG_GET_PRIVATE (name_selector_dialog);
606 }
607
608 /**
609  * e_name_selector_dialog_new:
610  * @registry: an #ESourceRegistry
611  *
612  * Creates a new #ENameSelectorDialog.
613  *
614  * Returns: A new #ENameSelectorDialog.
615  **/
616 ENameSelectorDialog *
617 e_name_selector_dialog_new (ESourceRegistry *registry)
618 {
619         g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
620
621         return g_object_new (
622                 E_TYPE_NAME_SELECTOR_DIALOG,
623                 "registry", registry, NULL);
624 }
625
626 /**
627  * e_name_selector_dialog_get_registry:
628  * @name_selector_dialog: an #ENameSelectorDialog
629  *
630  * Returns the #ESourceRegistry that was passed to
631  * e_name_selector_dialog_new().
632  *
633  * Returns: the #ESourceRegistry
634  *
635  * Since: 3.6
636  **/
637 ESourceRegistry *
638 e_name_selector_dialog_get_registry (ENameSelectorDialog *name_selector_dialog)
639 {
640         g_return_val_if_fail (
641                 E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog), NULL);
642
643         return name_selector_dialog->priv->registry;
644 }
645
646 /* --------- *
647  * Utilities *
648  * --------- */
649
650 static gchar *
651 escape_sexp_string (const gchar *string)
652 {
653         GString *gstring;
654         gchar   *encoded_string;
655
656         gstring = g_string_new ("");
657         e_sexp_encode_string (gstring, string);
658
659         encoded_string = gstring->str;
660         g_string_free (gstring, FALSE);
661
662         return encoded_string;
663 }
664
665 static void
666 sort_iter_to_contact_store_iter (ENameSelectorDialog *name_selector_dialog,
667                                  GtkTreeIter *iter,
668                                  gint *email_n)
669 {
670         ETreeModelGenerator *contact_filter;
671         GtkTreeIter          child_iter;
672         gint                 email_n_local;
673
674         contact_filter = e_name_selector_model_peek_contact_filter (
675                 name_selector_dialog->priv->name_selector_model);
676
677         gtk_tree_model_sort_convert_iter_to_child_iter (
678                 name_selector_dialog->priv->contact_sort, &child_iter, iter);
679         e_tree_model_generator_convert_iter_to_child_iter (
680                 contact_filter, iter, &email_n_local, &child_iter);
681
682         if (email_n)
683                 *email_n = email_n_local;
684 }
685
686 static void
687 add_destination (ENameSelectorModel *name_selector_model,
688                  EDestinationStore *destination_store,
689                  EContact *contact,
690                  gint email_n,
691                  EBookClient *client)
692 {
693         EDestination *destination;
694         GList *email_list, *nth;
695
696         /* get the correct index of an email in the contact */
697         email_list = e_name_selector_model_get_contact_emails_without_used (name_selector_model, contact, FALSE);
698         while (nth = g_list_nth (email_list, email_n), nth && nth->data == NULL) {
699                 email_n++;
700         }
701         e_name_selector_model_free_emails_list (email_list);
702
703         /* Transfer (actually, copy into a destination and let the model filter out the
704          * source automatically) */
705
706         destination = e_destination_new ();
707         e_destination_set_contact (destination, contact, email_n);
708         if (client)
709                 e_destination_set_client (destination, client);
710         e_destination_store_append_destination (destination_store, destination);
711         g_object_unref (destination);
712 }
713
714 static void
715 remove_books (ENameSelectorDialog *name_selector_dialog)
716 {
717         EContactStore *contact_store;
718         GSList        *clients, *l;
719
720         if (!name_selector_dialog->priv->name_selector_model)
721                 return;
722
723         contact_store = e_name_selector_model_peek_contact_store (
724                 name_selector_dialog->priv->name_selector_model);
725
726         /* Remove books (should be just one) being viewed */
727         clients = e_contact_store_get_clients (contact_store);
728         for (l = clients; l; l = g_slist_next (l)) {
729                 EBookClient *client = l->data;
730                 e_contact_store_remove_client (contact_store, client);
731         }
732         g_slist_free (clients);
733
734         /* See if we have a book pending; stop loading it if so */
735         if (name_selector_dialog->priv->cancellable != NULL) {
736                 g_cancellable_cancel (name_selector_dialog->priv->cancellable);
737                 g_object_unref (name_selector_dialog->priv->cancellable);
738                 name_selector_dialog->priv->cancellable = NULL;
739         }
740 }
741
742 /* ------------------ *
743  * Section management *
744  * ------------------ */
745
746 static gint
747 find_section_by_transfer_button (ENameSelectorDialog *name_selector_dialog,
748                                  GtkButton *transfer_button)
749 {
750         gint i;
751
752         for (i = 0; i < name_selector_dialog->priv->sections->len; i++) {
753                 Section *section = &g_array_index (
754                         name_selector_dialog->priv->sections, Section, i);
755
756                 if (section->transfer_button == transfer_button)
757                         return i;
758         }
759
760         return -1;
761 }
762
763 static gint
764 find_section_by_tree_view (ENameSelectorDialog *name_selector_dialog,
765                            GtkTreeView *tree_view)
766 {
767         gint i;
768
769         for (i = 0; i < name_selector_dialog->priv->sections->len; i++) {
770                 Section *section = &g_array_index (
771                         name_selector_dialog->priv->sections, Section, i);
772
773                 if (section->destination_view == tree_view)
774                         return i;
775         }
776
777         return -1;
778 }
779
780 static gint
781 find_section_by_name (ENameSelectorDialog *name_selector_dialog,
782                       const gchar *name)
783 {
784         gint i;
785
786         for (i = 0; i < name_selector_dialog->priv->sections->len; i++) {
787                 Section *section = &g_array_index (
788                         name_selector_dialog->priv->sections, Section, i);
789
790                 if (!strcmp (name, section->name))
791                         return i;
792         }
793
794         return -1;
795 }
796
797 static void
798 selection_changed (GtkTreeSelection *selection,
799                    SelData *data)
800 {
801         GtkTreeSelection *contact_selection;
802         gboolean          have_selection = FALSE;
803
804         contact_selection = gtk_tree_view_get_selection (data->view);
805         if (gtk_tree_selection_count_selected_rows (contact_selection) > 0)
806                 have_selection = TRUE;
807         gtk_widget_set_sensitive (GTK_WIDGET (data->button), have_selection);
808 }
809
810 static GtkTreeView *
811 make_tree_view_for_section (ENameSelectorDialog *name_selector_dialog,
812                             EDestinationStore *destination_store)
813 {
814         GtkTreeView *tree_view;
815         GtkTreeViewColumn *column;
816         GtkCellRenderer   *cell_renderer;
817
818         tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
819
820         column = gtk_tree_view_column_new ();
821         cell_renderer = GTK_CELL_RENDERER (gtk_cell_renderer_text_new ());
822         gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
823         gtk_tree_view_column_set_cell_data_func (
824                 column, cell_renderer,
825                 (GtkTreeCellDataFunc) destination_column_formatter,
826                 name_selector_dialog, NULL);
827         gtk_tree_view_append_column (tree_view, column);
828         gtk_tree_view_set_headers_visible (tree_view, FALSE);
829         gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (destination_store));
830
831         return tree_view;
832 }
833
834 static void
835 setup_section_button (ENameSelectorDialog *name_selector_dialog,
836                       GtkButton *button,
837                       double halign,
838                       const gchar *label_text,
839                       const gchar *icon_name,
840                       gboolean icon_before_label)
841 {
842         GtkWidget *alignment;
843         GtkWidget *hgrid;
844         GtkWidget *label;
845         GtkWidget *image;
846
847         gtk_size_group_add_widget (
848                 name_selector_dialog->priv->button_size_group,
849                 GTK_WIDGET (button));
850
851         alignment = gtk_alignment_new (halign, 0.5, 0.0, 0.0);
852         gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (alignment));
853
854         hgrid = g_object_new (GTK_TYPE_GRID,
855                 "orientation", GTK_ORIENTATION_HORIZONTAL,
856                 "row-homogeneous", FALSE,
857                 "column-spacing", 2,
858                 NULL);
859         gtk_widget_show (hgrid);
860         gtk_container_add (GTK_CONTAINER (alignment), hgrid);
861
862         label = gtk_label_new_with_mnemonic (label_text);
863         gtk_widget_show (label);
864
865         image = gtk_image_new_from_stock (icon_name, GTK_ICON_SIZE_BUTTON);
866         gtk_widget_show (image);
867
868         if (icon_before_label) {
869                 gtk_container_add (GTK_CONTAINER (hgrid), image);
870                 gtk_container_add (GTK_CONTAINER (hgrid), label);
871         } else {
872                 gtk_container_add (GTK_CONTAINER (hgrid), label);
873                 gtk_container_add (GTK_CONTAINER (hgrid), image);
874         }
875 }
876
877 static gint
878 add_section (ENameSelectorDialog *name_selector_dialog,
879              const gchar *name,
880              const gchar *pretty_name,
881              EDestinationStore *destination_store)
882 {
883         ENameSelectorDialogPrivate *priv;
884         Section            section;
885         GtkWidget         *vgrid;
886         GtkWidget         *alignment;
887         GtkWidget         *scrollwin;
888         SelData           *data;
889         GtkTreeSelection  *selection;
890         gchar             *text;
891         GtkWidget         *hgrid;
892
893         g_assert (name != NULL);
894         g_assert (pretty_name != NULL);
895         g_assert (E_IS_DESTINATION_STORE (destination_store));
896
897         priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (name_selector_dialog);
898
899         memset (&section, 0, sizeof (Section));
900
901         section.name = g_strdup (name);
902         section.section_grid = g_object_new (GTK_TYPE_GRID,
903                 "orientation", GTK_ORIENTATION_HORIZONTAL,
904                 "row-homogeneous", FALSE,
905                 "column-spacing", 12,
906                 "vexpand", TRUE,
907                 "valign", GTK_ALIGN_FILL,
908                 NULL);
909         section.label = GTK_LABEL (gtk_label_new_with_mnemonic (pretty_name));
910         section.transfer_button  = GTK_BUTTON (gtk_button_new ());
911         section.remove_button  = GTK_BUTTON (gtk_button_new ());
912         section.destination_view = make_tree_view_for_section (name_selector_dialog, destination_store);
913
914         gtk_label_set_mnemonic_widget (GTK_LABEL (section.label), GTK_WIDGET (section.destination_view));
915
916         if (pango_parse_markup (pretty_name, -1, '_', NULL,
917                                 &text, NULL, NULL))  {
918                 atk_object_set_name (gtk_widget_get_accessible (
919                                         GTK_WIDGET (section.destination_view)), text);
920                 g_free (text);
921         }
922
923         /* Set up transfer button */
924         g_signal_connect_swapped (
925                 section.transfer_button, "clicked",
926                 G_CALLBACK (transfer_button_clicked), name_selector_dialog);
927
928         /*data for the remove callback*/
929         data = g_malloc0 (sizeof (SelData));
930         data->view = section.destination_view;
931         data->dlg_ptr = name_selector_dialog;
932
933         /*Associate to an object destroy so that it gets freed*/
934         g_object_set_data_full ((GObject *) section.destination_view, "sel-remove-data", data, g_free);
935
936         g_signal_connect (
937                 section.remove_button, "clicked",
938                 G_CALLBACK (remove_button_clicked), data);
939
940         /* Alignment and vgrid for the add/remove buttons */
941
942         alignment = gtk_alignment_new (0.5, 0.0, 0.0, 0.0);
943         gtk_container_add (GTK_CONTAINER (section.section_grid), alignment);
944
945         vgrid = g_object_new (GTK_TYPE_GRID,
946                 "orientation", GTK_ORIENTATION_VERTICAL,
947                 "column-homogeneous", TRUE,
948                 "row-spacing", 6,
949                 NULL);
950
951         gtk_container_add (GTK_CONTAINER (alignment), vgrid);
952
953         /* "Add" button */
954         gtk_container_add (GTK_CONTAINER (vgrid), GTK_WIDGET (section.transfer_button));
955         setup_section_button (name_selector_dialog, section.transfer_button, 0.7, _("_Add"), "gtk-go-forward", FALSE);
956
957         /* "Remove" button */
958         gtk_container_add (GTK_CONTAINER (vgrid), GTK_WIDGET (section.remove_button));
959         setup_section_button (name_selector_dialog, section.remove_button, 0.5, _("_Remove"), "gtk-go-back", TRUE);
960         gtk_widget_set_sensitive (GTK_WIDGET (section.remove_button), FALSE);
961
962         /* hgrid for label and scrolled window. This is a separate hgrid, instead
963          * of just using the section.section_grid directly, as it has a different
964          * spacing.
965          */
966
967         hgrid = g_object_new (GTK_TYPE_GRID,
968                 "orientation", GTK_ORIENTATION_HORIZONTAL,
969                 "row-homogeneous", FALSE,
970                 "column-spacing", 6,
971                 "vexpand", TRUE,
972                 "valign", GTK_ALIGN_FILL,
973                 NULL);
974         gtk_container_add (GTK_CONTAINER (section.section_grid), hgrid);
975
976         /* Title label */
977
978         gtk_size_group_add_widget (priv->dest_label_size_group, GTK_WIDGET (section.label));
979
980         gtk_misc_set_alignment (GTK_MISC (section.label), 0.0, 0.0);
981         gtk_container_add (GTK_CONTAINER (hgrid), GTK_WIDGET (section.label));
982
983         /* Treeview in a scrolled window */
984         scrollwin = gtk_scrolled_window_new (NULL, NULL);
985         gtk_container_add (GTK_CONTAINER (hgrid), scrollwin);
986         gtk_widget_set_hexpand (scrollwin, TRUE);
987         gtk_widget_set_halign (scrollwin, GTK_ALIGN_FILL);
988         gtk_widget_set_vexpand (scrollwin, TRUE);
989         gtk_widget_set_valign (scrollwin, GTK_ALIGN_FILL);
990         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
991         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin), GTK_SHADOW_IN);
992         gtk_container_add (GTK_CONTAINER (scrollwin), GTK_WIDGET (section.destination_view));
993
994         /*data for 'changed' callback*/
995         data = g_malloc0 (sizeof (SelData));
996         data->view = section.destination_view;
997         data->button = section.remove_button;
998         g_object_set_data_full ((GObject *) section.destination_view, "sel-change-data", data, g_free);
999         selection = gtk_tree_view_get_selection (section.destination_view);
1000         gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1001
1002         g_signal_connect (
1003                 selection, "changed",
1004                 G_CALLBACK (selection_changed), data);
1005
1006         g_signal_connect_swapped (
1007                 section.destination_view, "row-activated",
1008                 G_CALLBACK (destination_activated), name_selector_dialog);
1009         g_signal_connect_swapped (
1010                 section.destination_view, "key-press-event",
1011                 G_CALLBACK (destination_key_press), name_selector_dialog);
1012
1013         /* Done! */
1014
1015         gtk_widget_show_all (GTK_WIDGET (section.section_grid));
1016
1017         /* Pack this section's box into the dialog */
1018         gtk_container_add (GTK_CONTAINER (name_selector_dialog->priv->destination_vgrid), GTK_WIDGET (section.section_grid));
1019         g_object_set (G_OBJECT (section.section_grid),
1020                 "vexpand", TRUE,
1021                 "valign", GTK_ALIGN_FILL,
1022                 NULL);
1023
1024         g_array_append_val (name_selector_dialog->priv->sections, section);
1025
1026         /* Make sure UI is consistent */
1027         contact_selection_changed (name_selector_dialog);
1028
1029         return name_selector_dialog->priv->sections->len - 1;
1030 }
1031
1032 static void
1033 free_section (ENameSelectorDialog *name_selector_dialog,
1034               gint n)
1035 {
1036         Section *section;
1037
1038         g_assert (n >= 0);
1039         g_assert (n < name_selector_dialog->priv->sections->len);
1040
1041         section = &g_array_index (
1042                 name_selector_dialog->priv->sections, Section, n);
1043
1044         g_free (section->name);
1045         gtk_widget_destroy (GTK_WIDGET (section->section_grid));
1046 }
1047
1048 static void
1049 model_section_added (ENameSelectorDialog *name_selector_dialog,
1050                      const gchar *name)
1051 {
1052         gchar             *pretty_name;
1053         EDestinationStore *destination_store;
1054
1055         e_name_selector_model_peek_section (
1056                 name_selector_dialog->priv->name_selector_model,
1057                 name, &pretty_name, &destination_store);
1058         add_section (name_selector_dialog, name, pretty_name, destination_store);
1059         g_free (pretty_name);
1060 }
1061
1062 static void
1063 model_section_removed (ENameSelectorDialog *name_selector_dialog,
1064                        const gchar *name)
1065 {
1066         gint section_index;
1067
1068         section_index = find_section_by_name (name_selector_dialog, name);
1069         g_assert (section_index >= 0);
1070
1071         free_section (name_selector_dialog, section_index);
1072         g_array_remove_index (
1073                 name_selector_dialog->priv->sections, section_index);
1074 }
1075
1076 /* -------------------- *
1077  * Addressbook selector *
1078  * -------------------- */
1079
1080 static void
1081 view_progress (EBookClientView *view,
1082                guint percent,
1083                const gchar *message,
1084                ENameSelectorDialog *dialog)
1085 {
1086         if (message == NULL)
1087                 gtk_label_set_text (dialog->priv->status_label, "");
1088         else
1089                 gtk_label_set_text (dialog->priv->status_label, message);
1090 }
1091
1092 static void
1093 view_complete (EBookClientView *view,
1094                const GError *error,
1095                ENameSelectorDialog *dialog)
1096 {
1097         view_progress (view, -1, NULL, dialog);
1098 }
1099
1100 static void
1101 start_client_view_cb (EContactStore *store,
1102                       EBookClientView *client_view,
1103                       ENameSelectorDialog *name_selector_dialog)
1104 {
1105         g_signal_connect (
1106                 client_view, "progress",
1107                 G_CALLBACK (view_progress), name_selector_dialog);
1108
1109         g_signal_connect (
1110                 client_view, "complete",
1111                 G_CALLBACK (view_complete), name_selector_dialog);
1112 }
1113
1114 static void
1115 stop_client_view_cb (EContactStore *store,
1116                      EBookClientView *client_view,
1117                      ENameSelectorDialog *name_selector_dialog)
1118 {
1119         g_signal_handlers_disconnect_by_func (client_view, view_progress, name_selector_dialog);
1120         g_signal_handlers_disconnect_by_func (client_view, view_complete, name_selector_dialog);
1121 }
1122
1123 static void
1124 book_loaded_cb (GObject *source_object,
1125                 GAsyncResult *result,
1126                 gpointer user_data)
1127 {
1128         ENameSelectorDialog *name_selector_dialog = user_data;
1129         EClient *client = NULL;
1130         EBookClient *book_client;
1131         EContactStore *store;
1132         ENameSelectorModel *model;
1133         GError *error = NULL;
1134
1135         e_client_utils_open_new_finish (E_SOURCE (source_object), result, &client, &error);
1136
1137         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1138                 g_warn_if_fail (client == NULL);
1139                 g_error_free (error);
1140                 goto exit;
1141         }
1142
1143         if (error != NULL) {
1144                 gchar *message;
1145
1146                 message = g_strdup_printf (
1147                         _("Error loading address book: %s"), error->message);
1148                 gtk_label_set_text (
1149                         name_selector_dialog->priv->status_label, message);
1150                 g_free (message);
1151
1152                 g_warn_if_fail (client == NULL);
1153                 g_error_free (error);
1154                 goto exit;
1155         }
1156
1157         book_client = E_BOOK_CLIENT (client);
1158         if (!book_client) {
1159                 g_warn_if_fail (book_client != NULL);
1160                 goto exit;
1161         }
1162
1163         model = name_selector_dialog->priv->name_selector_model;
1164         store = e_name_selector_model_peek_contact_store (model);
1165         e_contact_store_add_client (store, book_client);
1166         g_object_unref (book_client);
1167
1168  exit:
1169         g_object_unref (name_selector_dialog);
1170 }
1171
1172 static void
1173 source_changed (ENameSelectorDialog *name_selector_dialog,
1174                 ESourceComboBox *source_combo_box)
1175 {
1176         GCancellable *cancellable;
1177         ESource *source;
1178         gpointer parent;
1179
1180         source = e_source_combo_box_ref_active (source_combo_box);
1181
1182         parent = gtk_widget_get_toplevel (GTK_WIDGET (name_selector_dialog));
1183         parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
1184
1185         /* Remove any previous books being shown or loaded */
1186         remove_books (name_selector_dialog);
1187
1188         if (source == NULL)
1189                 return;
1190
1191         cancellable = g_cancellable_new ();
1192         name_selector_dialog->priv->cancellable = cancellable;
1193
1194         /* Start loading selected book */
1195         e_client_utils_open_new (
1196                 source, E_CLIENT_SOURCE_TYPE_CONTACTS, TRUE, cancellable,
1197                 book_loaded_cb, g_object_ref (name_selector_dialog));
1198
1199         g_object_unref (source);
1200 }
1201
1202 /* --------------- *
1203  * Other UI events *
1204  * --------------- */
1205
1206 static void
1207 search_changed (ENameSelectorDialog *name_selector_dialog)
1208 {
1209         ENameSelectorDialogPrivate *priv = E_NAME_SELECTOR_DIALOG_GET_PRIVATE (name_selector_dialog);
1210         EContactStore *contact_store;
1211         EBookQuery    *book_query;
1212         GtkWidget     *combo_box;
1213         const gchar   *text;
1214         gchar         *text_escaped;
1215         gchar         *query_string;
1216         gchar         *category;
1217         gchar         *category_escaped;
1218         gchar         *user_fields_str;
1219
1220         combo_box = priv->category_combobox;
1221         if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) == -1)
1222                 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
1223
1224         category = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo_box));
1225         category_escaped = escape_sexp_string (category);
1226
1227         text = gtk_entry_get_text (name_selector_dialog->priv->search_entry);
1228         text_escaped = escape_sexp_string (text);
1229
1230         user_fields_str = ens_util_populate_user_query_fields (priv->user_query_fields, text, text_escaped);
1231
1232         if (g_strcmp0 (category, _("Any Category")) == 0)
1233                 query_string = g_strdup_printf (
1234                         "(or (beginswith \"file_as\" %s) "
1235                         "    (beginswith \"full_name\" %s) "
1236                         "    (beginswith \"email\" %s) "
1237                         "    (beginswith \"nickname\" %s)%s))",
1238                         text_escaped, text_escaped,
1239                         text_escaped, text_escaped,
1240                         user_fields_str ? user_fields_str : "");
1241         else
1242                 query_string = g_strdup_printf (
1243                         "(and (is \"category_list\" %s) "
1244                         "(or (beginswith \"file_as\" %s) "
1245                         "    (beginswith \"full_name\" %s) "
1246                         "    (beginswith \"email\" %s) "
1247                         "    (beginswith \"nickname\" %s)%s))",
1248                         category_escaped, text_escaped, text_escaped,
1249                         text_escaped, text_escaped,
1250                         user_fields_str ? user_fields_str : "");
1251
1252         book_query = e_book_query_from_string (query_string);
1253
1254         contact_store = e_name_selector_model_peek_contact_store (
1255                 name_selector_dialog->priv->name_selector_model);
1256         e_contact_store_set_query (contact_store, book_query);
1257         e_book_query_unref (book_query);
1258
1259         g_free (query_string);
1260         g_free (text_escaped);
1261         g_free (category_escaped);
1262         g_free (category);
1263         g_free (user_fields_str);
1264 }
1265
1266 static void
1267 contact_selection_changed (ENameSelectorDialog *name_selector_dialog)
1268 {
1269         GtkTreeSelection *contact_selection;
1270         gboolean          have_selection = FALSE;
1271         gint              i;
1272
1273         contact_selection = gtk_tree_view_get_selection (
1274                 name_selector_dialog->priv->contact_view);
1275         if (gtk_tree_selection_count_selected_rows (contact_selection))
1276                 have_selection = TRUE;
1277
1278         for (i = 0; i < name_selector_dialog->priv->sections->len; i++) {
1279                 Section *section = &g_array_index (
1280                         name_selector_dialog->priv->sections, Section, i);
1281                 gtk_widget_set_sensitive (GTK_WIDGET (section->transfer_button), have_selection);
1282         }
1283 }
1284
1285 static void
1286 contact_activated (ENameSelectorDialog *name_selector_dialog,
1287                    GtkTreePath *path)
1288 {
1289         EContactStore     *contact_store;
1290         EDestinationStore *destination_store;
1291         EContact          *contact;
1292         GtkTreeIter       iter;
1293         Section           *section;
1294         gint               email_n;
1295
1296         /* When a contact is activated, we transfer it to the first destination on our list */
1297
1298         contact_store = e_name_selector_model_peek_contact_store (
1299                 name_selector_dialog->priv->name_selector_model);
1300
1301         /* If we have no sections, we can't transfer */
1302         if (name_selector_dialog->priv->sections->len == 0)
1303                 return;
1304
1305         /* Get the contact to be transferred */
1306
1307         if (!gtk_tree_model_get_iter (
1308                 GTK_TREE_MODEL (name_selector_dialog->priv->contact_sort),
1309                 &iter, path))
1310                 g_assert_not_reached ();
1311
1312         sort_iter_to_contact_store_iter (name_selector_dialog, &iter, &email_n);
1313
1314         contact = e_contact_store_get_contact (contact_store, &iter);
1315         if (!contact) {
1316                 g_warning ("ENameSelectorDialog could not get selected contact!");
1317                 return;
1318         }
1319
1320         section = &g_array_index (
1321                 name_selector_dialog->priv->sections,
1322                 Section, name_selector_dialog->priv->destination_index);
1323         if (!e_name_selector_model_peek_section (
1324                 name_selector_dialog->priv->name_selector_model,
1325                 section->name, NULL, &destination_store)) {
1326                 g_warning ("ENameSelectorDialog has a section unknown to the model!");
1327                 return;
1328         }
1329
1330         add_destination (
1331                 name_selector_dialog->priv->name_selector_model,
1332                 destination_store, contact, email_n,
1333                 e_contact_store_get_client (contact_store, &iter));
1334 }
1335
1336 static void
1337 destination_activated (ENameSelectorDialog *name_selector_dialog,
1338                        GtkTreePath *path,
1339                        GtkTreeViewColumn *column,
1340                        GtkTreeView *tree_view)
1341 {
1342         gint               section_index;
1343         EDestinationStore *destination_store;
1344         EDestination      *destination;
1345         Section           *section;
1346         GtkTreeIter        iter;
1347
1348         /* When a destination is activated, we remove it from the section */
1349
1350         section_index = find_section_by_tree_view (
1351                 name_selector_dialog, tree_view);
1352         if (section_index < 0) {
1353                 g_warning ("ENameSelectorDialog got activation from unknown view!");
1354                 return;
1355         }
1356
1357         section = &g_array_index (
1358                 name_selector_dialog->priv->sections, Section, section_index);
1359         if (!e_name_selector_model_peek_section (
1360                 name_selector_dialog->priv->name_selector_model,
1361                 section->name, NULL, &destination_store)) {
1362                 g_warning ("ENameSelectorDialog has a section unknown to the model!");
1363                 return;
1364         }
1365
1366         if (!gtk_tree_model_get_iter (
1367                 GTK_TREE_MODEL (destination_store), &iter, path))
1368                 g_assert_not_reached ();
1369
1370         destination = e_destination_store_get_destination (
1371                 destination_store, &iter);
1372         g_assert (destination);
1373
1374         e_destination_store_remove_destination (
1375                 destination_store, destination);
1376 }
1377
1378 static gboolean
1379 remove_selection (ENameSelectorDialog *name_selector_dialog,
1380                   GtkTreeView *tree_view)
1381 {
1382         gint               section_index;
1383         EDestinationStore *destination_store;
1384         EDestination      *destination;
1385         Section           *section;
1386         GtkTreeSelection  *selection;
1387         GList             *rows, *l;
1388
1389         section_index = find_section_by_tree_view (
1390                 name_selector_dialog, tree_view);
1391         if (section_index < 0) {
1392                 g_warning ("ENameSelectorDialog got key press from unknown view!");
1393                 return FALSE;
1394         }
1395
1396         section = &g_array_index (
1397                 name_selector_dialog->priv->sections, Section, section_index);
1398         if (!e_name_selector_model_peek_section (
1399                 name_selector_dialog->priv->name_selector_model,
1400                 section->name, NULL, &destination_store)) {
1401                 g_warning ("ENameSelectorDialog has a section unknown to the model!");
1402                 return FALSE;
1403         }
1404
1405         selection = gtk_tree_view_get_selection (tree_view);
1406         if (!gtk_tree_selection_count_selected_rows (selection)) {
1407                 g_warning ("ENameSelectorDialog remove button clicked, but no selection!");
1408                 return FALSE;
1409         }
1410
1411         rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1412         rows = g_list_reverse (rows);
1413
1414         for (l = rows; l; l = g_list_next (l)) {
1415                 GtkTreeIter iter;
1416                 GtkTreePath *path = l->data;
1417
1418                 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store),
1419                                               &iter, path))
1420                         g_assert_not_reached ();
1421
1422                 gtk_tree_path_free (path);
1423
1424                 destination = e_destination_store_get_destination (
1425                         destination_store, &iter);
1426                 g_assert (destination);
1427
1428                 e_destination_store_remove_destination (
1429                         destination_store, destination);
1430         }
1431         g_list_free (rows);
1432
1433         return TRUE;
1434 }
1435
1436 static void
1437 remove_button_clicked (GtkButton *button,
1438                        SelData *data)
1439 {
1440         GtkTreeView *view;
1441         ENameSelectorDialog *name_selector_dialog;
1442
1443         view = data->view;
1444         name_selector_dialog = data->dlg_ptr;
1445         remove_selection (name_selector_dialog, view);
1446 }
1447
1448 static gboolean
1449 destination_key_press (ENameSelectorDialog *name_selector_dialog,
1450                        GdkEventKey *event,
1451                        GtkTreeView *tree_view)
1452 {
1453
1454         /* we only care about DEL key */
1455         if (event->keyval != GDK_KEY_Delete)
1456                 return FALSE;
1457         return remove_selection (name_selector_dialog, tree_view);
1458
1459 }
1460
1461 static void
1462 transfer_button_clicked (ENameSelectorDialog *name_selector_dialog,
1463                          GtkButton *transfer_button)
1464 {
1465         EContactStore     *contact_store;
1466         EDestinationStore *destination_store;
1467         GtkTreeSelection  *selection;
1468         EContact          *contact;
1469         gint               section_index;
1470         Section           *section;
1471         gint               email_n;
1472         GList             *rows, *l;
1473
1474         /* Get the contact to be transferred */
1475
1476         contact_store = e_name_selector_model_peek_contact_store (
1477                 name_selector_dialog->priv->name_selector_model);
1478         selection = gtk_tree_view_get_selection (
1479                 name_selector_dialog->priv->contact_view);
1480
1481         if (!gtk_tree_selection_count_selected_rows (selection)) {
1482                 g_warning ("ENameSelectorDialog transfer button clicked, but no selection!");
1483                 return;
1484         }
1485
1486         /* Get the target section */
1487         section_index = find_section_by_transfer_button (
1488                 name_selector_dialog, transfer_button);
1489         if (section_index < 0) {
1490                 g_warning ("ENameSelectorDialog got click from unknown button!");
1491                 return;
1492         }
1493
1494         section = &g_array_index (
1495                 name_selector_dialog->priv->sections, Section, section_index);
1496         if (!e_name_selector_model_peek_section (
1497                 name_selector_dialog->priv->name_selector_model,
1498                 section->name, NULL, &destination_store)) {
1499                 g_warning ("ENameSelectorDialog has a section unknown to the model!");
1500                 return;
1501         }
1502
1503         rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1504         rows = g_list_reverse (rows);
1505
1506         for (l = rows; l; l = g_list_next (l)) {
1507                 GtkTreeIter iter;
1508                 GtkTreePath *path = l->data;
1509
1510                 if (!gtk_tree_model_get_iter (
1511                         GTK_TREE_MODEL (name_selector_dialog->priv->contact_sort),
1512                         &iter, path)) {
1513                         gtk_tree_path_free (path);
1514                         return;
1515                 }
1516
1517                 gtk_tree_path_free (path);
1518                 sort_iter_to_contact_store_iter (name_selector_dialog, &iter, &email_n);
1519
1520                 contact = e_contact_store_get_contact (contact_store, &iter);
1521                 if (!contact) {
1522                         g_warning ("ENameSelectorDialog could not get selected contact!");
1523                         g_list_free (rows);
1524                         return;
1525                 }
1526
1527                 add_destination (
1528                         name_selector_dialog->priv->name_selector_model,
1529                         destination_store, contact, email_n,
1530                         e_contact_store_get_client (contact_store, &iter));
1531         }
1532         g_list_free (rows);
1533 }
1534
1535 /* --------------------- *
1536  * Main model management *
1537  * --------------------- */
1538
1539 static void
1540 setup_name_selector_model (ENameSelectorDialog *name_selector_dialog)
1541 {
1542         ETreeModelGenerator *contact_filter;
1543         EContactStore       *contact_store;
1544         GList               *new_sections;
1545         GList               *l;
1546
1547         /* Create new destination sections in UI */
1548
1549         new_sections = e_name_selector_model_list_sections (
1550                 name_selector_dialog->priv->name_selector_model);
1551
1552         for (l = new_sections; l; l = g_list_next (l)) {
1553                 gchar             *name = l->data;
1554                 gchar             *pretty_name;
1555                 EDestinationStore *destination_store;
1556
1557                 e_name_selector_model_peek_section (
1558                         name_selector_dialog->priv->name_selector_model,
1559                         name, &pretty_name, &destination_store);
1560
1561                 add_section (name_selector_dialog, name, pretty_name, destination_store);
1562
1563                 g_free (pretty_name);
1564                 g_free (name);
1565         }
1566
1567         g_list_free (new_sections);
1568
1569         /* Connect to section add/remove signals */
1570
1571         g_signal_connect_swapped (
1572                 name_selector_dialog->priv->name_selector_model, "section-added",
1573                 G_CALLBACK (model_section_added), name_selector_dialog);
1574         g_signal_connect_swapped (
1575                 name_selector_dialog->priv->name_selector_model, "section-removed",
1576                 G_CALLBACK (model_section_removed), name_selector_dialog);
1577
1578         /* Get contact store and its filter wrapper */
1579
1580         contact_filter = e_name_selector_model_peek_contact_filter (
1581                 name_selector_dialog->priv->name_selector_model);
1582
1583         /* Create sorting model on top of filter, assign it to view */
1584
1585         name_selector_dialog->priv->contact_sort = GTK_TREE_MODEL_SORT (
1586                 gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (contact_filter)));
1587
1588         /* sort on full name as we display full name in name selector dialog */
1589         gtk_tree_sortable_set_sort_column_id (
1590                 GTK_TREE_SORTABLE (name_selector_dialog->priv->contact_sort),
1591                 E_CONTACT_FULL_NAME, GTK_SORT_ASCENDING);
1592
1593         gtk_tree_view_set_model (
1594                 name_selector_dialog->priv->contact_view,
1595                 GTK_TREE_MODEL (name_selector_dialog->priv->contact_sort));
1596
1597         contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->priv->name_selector_model);
1598         if (contact_store) {
1599                 g_signal_connect (contact_store, "start-client-view", G_CALLBACK (start_client_view_cb), name_selector_dialog);
1600                 g_signal_connect (contact_store, "stop-client-view", G_CALLBACK (stop_client_view_cb), name_selector_dialog);
1601         }
1602
1603         /* Make sure UI is consistent */
1604
1605         search_changed (name_selector_dialog);
1606         contact_selection_changed (name_selector_dialog);
1607 }
1608
1609 static void
1610 shutdown_name_selector_model (ENameSelectorDialog *name_selector_dialog)
1611 {
1612         gint i;
1613
1614         /* Rid UI of previous destination sections */
1615
1616         for (i = 0; i < name_selector_dialog->priv->sections->len; i++)
1617                 free_section (name_selector_dialog, i);
1618
1619         g_array_set_size (name_selector_dialog->priv->sections, 0);
1620
1621         /* Free sorting model */
1622
1623         if (name_selector_dialog->priv->contact_sort) {
1624                 g_object_unref (name_selector_dialog->priv->contact_sort);
1625                 name_selector_dialog->priv->contact_sort = NULL;
1626         }
1627
1628         /* Free backend model */
1629
1630         if (name_selector_dialog->priv->name_selector_model) {
1631                 EContactStore *contact_store;
1632
1633                 contact_store = e_name_selector_model_peek_contact_store (name_selector_dialog->priv->name_selector_model);
1634                 if (contact_store) {
1635                         g_signal_handlers_disconnect_by_func (contact_store, start_client_view_cb, name_selector_dialog);
1636                         g_signal_handlers_disconnect_by_func (contact_store, stop_client_view_cb, name_selector_dialog);
1637                 }
1638
1639                 g_signal_handlers_disconnect_matched (
1640                         name_selector_dialog->priv->name_selector_model,
1641                         G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_dialog);
1642
1643                 g_object_unref (name_selector_dialog->priv->name_selector_model);
1644                 name_selector_dialog->priv->name_selector_model = NULL;
1645         }
1646 }
1647
1648 static void
1649 contact_column_formatter (GtkTreeViewColumn *column,
1650                           GtkCellRenderer *cell,
1651                           GtkTreeModel *model,
1652                           GtkTreeIter *iter,
1653                           ENameSelectorDialog *name_selector_dialog)
1654 {
1655         EContactStore *contact_store;
1656         EContact      *contact;
1657         GtkTreeIter    contact_store_iter;
1658         GList         *email_list;
1659         gchar         *string;
1660         gchar         *full_name_str;
1661         gchar         *email_str;
1662         gint           email_n;
1663
1664         contact_store_iter = *iter;
1665         sort_iter_to_contact_store_iter (
1666                 name_selector_dialog, &contact_store_iter, &email_n);
1667
1668         contact_store = e_name_selector_model_peek_contact_store (
1669                 name_selector_dialog->priv->name_selector_model);
1670         contact = e_contact_store_get_contact (
1671                 contact_store, &contact_store_iter);
1672         email_list = e_name_selector_model_get_contact_emails_without_used (
1673                 name_selector_dialog->priv->name_selector_model, contact, TRUE);
1674         email_str = g_list_nth_data (email_list, email_n);
1675         full_name_str = e_contact_get (contact, E_CONTACT_FULL_NAME);
1676
1677         if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
1678                 if (!full_name_str)
1679                         full_name_str = e_contact_get (contact, E_CONTACT_FILE_AS);
1680                 string = g_strdup_printf ("%s", full_name_str ? full_name_str : "?");
1681         } else {
1682                 string = g_strdup_printf (
1683                         "%s%s<%s>", full_name_str ? full_name_str : "",
1684                         full_name_str ? " " : "",
1685                         email_str ? email_str : "");
1686         }
1687
1688         g_free (full_name_str);
1689         e_name_selector_model_free_emails_list (email_list);
1690
1691         g_object_set (cell, "text", string, NULL);
1692         g_free (string);
1693 }
1694
1695 static void
1696 destination_column_formatter (GtkTreeViewColumn *column,
1697                               GtkCellRenderer *cell,
1698                               GtkTreeModel *model,
1699                               GtkTreeIter *iter,
1700                               ENameSelectorDialog *name_selector_dialog)
1701 {
1702         EDestinationStore *destination_store = E_DESTINATION_STORE (model);
1703         EDestination      *destination;
1704         GString           *buffer;
1705
1706         destination = e_destination_store_get_destination (destination_store, iter);
1707         g_assert (destination);
1708
1709         buffer = g_string_new (e_destination_get_name (destination));
1710
1711         if (!e_destination_is_evolution_list (destination)) {
1712                 const gchar *email;
1713
1714                 email = e_destination_get_email (destination);
1715                 if (email == NULL || *email == '\0')
1716                         email = "?";
1717                 g_string_append_printf (buffer, " <%s>", email);
1718         }
1719
1720         g_object_set (cell, "text", buffer->str, NULL);
1721         g_string_free (buffer, TRUE);
1722 }
1723
1724 /* ----------------------- *
1725  * ENameSelectorDialog API *
1726  * ----------------------- */
1727
1728 /**
1729  * e_name_selector_dialog_peek_model:
1730  * @name_selector_dialog: an #ENameSelectorDialog
1731  *
1732  * Gets the #ENameSelectorModel used by @name_selector_model.
1733  *
1734  * Returns: The #ENameSelectorModel being used.
1735  **/
1736 ENameSelectorModel *
1737 e_name_selector_dialog_peek_model (ENameSelectorDialog *name_selector_dialog)
1738 {
1739         g_return_val_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog), NULL);
1740
1741         return name_selector_dialog->priv->name_selector_model;
1742 }
1743
1744 /**
1745  * e_name_selector_dialog_set_model:
1746  * @name_selector_dialog: an #ENameSelectorDialog
1747  * @model: an #ENameSelectorModel
1748  *
1749  * Sets the model being used by @name_selector_dialog to @model.
1750  **/
1751 void
1752 e_name_selector_dialog_set_model (ENameSelectorDialog *name_selector_dialog,
1753                                   ENameSelectorModel *model)
1754 {
1755         g_return_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog));
1756         g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (model));
1757
1758         if (model == name_selector_dialog->priv->name_selector_model)
1759                 return;
1760
1761         shutdown_name_selector_model (name_selector_dialog);
1762         name_selector_dialog->priv->name_selector_model = g_object_ref (model);
1763
1764         setup_name_selector_model (name_selector_dialog);
1765 }
1766
1767 /**
1768  * e_name_selector_dialog_set_destination_index:
1769  * @name_selector_dialog: an #ENameSelectorDialog
1770  * @index: index of the destination section, starting from 0.
1771  *
1772  * Sets the index number of the destination section.
1773  **/
1774 void
1775 e_name_selector_dialog_set_destination_index (ENameSelectorDialog *name_selector_dialog,
1776                                               guint index)
1777 {
1778         g_return_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog));
1779
1780         if (index >= name_selector_dialog->priv->sections->len)
1781                 return;
1782
1783         name_selector_dialog->priv->destination_index = index;
1784 }
1785
1786 /**
1787  * e_name_selector_dialog_set_scrolling_policy:
1788  * @name_selector_dialog: an #ENameSelectorDialog
1789  * @hscrollbar_policy: scrolling policy for horizontal bar of the contacts window.
1790  * @vscrollbar_policy: scrolling policy for vertical bar of the contacts window.
1791  *
1792  * Sets the scrolling policy for the contacts section.
1793  *
1794  * Since: 3.2
1795  **/
1796 void
1797 e_name_selector_dialog_set_scrolling_policy (ENameSelectorDialog *name_selector_dialog,
1798                                              GtkPolicyType hscrollbar_policy,
1799                                              GtkPolicyType vscrollbar_policy)
1800 {
1801         GtkScrolledWindow *win = GTK_SCROLLED_WINDOW (name_selector_dialog->priv->contact_window);
1802
1803         gtk_scrolled_window_set_policy (win, hscrollbar_policy, vscrollbar_policy);
1804 }
1805
1806 /**
1807  * e_name_selector_dialog_get_section_visible:
1808  * @name_selector_dialog: an #ENameSelectorDialog
1809  * @name: name of the section
1810  *
1811  * Returns: whether section named @name is visible in the dialog.
1812  *
1813  * Since: 3.8
1814  **/
1815 gboolean
1816 e_name_selector_dialog_get_section_visible (ENameSelectorDialog *name_selector_dialog,
1817                                             const gchar *name)
1818 {
1819         Section *section;
1820         gint index;
1821
1822         g_return_val_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog), FALSE);
1823         g_return_val_if_fail (name != NULL, FALSE);
1824
1825         index = find_section_by_name (name_selector_dialog, name);
1826         g_return_val_if_fail (index != -1, FALSE);
1827
1828         section = &g_array_index (name_selector_dialog->priv->sections, Section, index);
1829         return gtk_widget_get_visible (GTK_WIDGET (section->section_grid));
1830 }
1831
1832 /**
1833  * e_name_selector_dialog_set_section_visible:
1834  * @name_selector_dialog: an #ENameSelectorDialog
1835  * @name: name of the section
1836  * @visible: whether to show or hide the section
1837  *
1838  * Shows or hides section named @name in the dialog.
1839  *
1840  * Since: 3.8
1841  **/
1842 void
1843 e_name_selector_dialog_set_section_visible (ENameSelectorDialog *name_selector_dialog,
1844                                             const gchar *name,
1845                                             gboolean visible)
1846 {
1847         Section *section;
1848         gint index;
1849
1850         g_return_if_fail (E_IS_NAME_SELECTOR_DIALOG (name_selector_dialog));
1851         g_return_if_fail (name != NULL);
1852
1853         index = find_section_by_name (name_selector_dialog, name);
1854         g_return_if_fail (index != -1);
1855
1856         section = &g_array_index (name_selector_dialog->priv->sections, Section, index);
1857
1858         if (visible)
1859                 gtk_widget_show (GTK_WIDGET (section->section_grid));
1860         else
1861                 gtk_widget_hide (GTK_WIDGET (section->section_grid));
1862 }
1863