Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserverui / e-source-selector.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* e-source-selector.c
3  *
4  * Copyright (C) 2003  Ximian, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
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 program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Author: Ettore Perazzoli <ettore@ximian.com>
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtktreeselection.h>
31 #include <gtk/gtktreestore.h>
32 #include <gtk/gtkcellrenderertoggle.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtkcellrendererpixbuf.h>
35
36 #include "e-data-server-ui-marshal.h"
37 #include "e-source-selector.h"
38
39 struct _ESourceSelectorPrivate {
40         ESourceList *list;
41
42         GtkTreeStore *tree_store;
43         
44         GHashTable *selected_sources;
45         GtkTreeRowReference *saved_primary_selection;
46         ESourceGroup *primary_source_group;
47         
48         int rebuild_model_idle_id;
49
50         gboolean toggled_last;
51         gboolean checkboxes_shown;
52         gboolean select_new;
53 };
54
55 typedef struct {
56         ESourceSelector *selector;
57
58         GHashTable *remaining_uids;
59         GSList *deleted_uids;
60
61         gboolean selection_changed;
62 } ESourceSelectorRebuildData;
63
64 enum {
65         SELECTION_CHANGED,
66         PRIMARY_SELECTION_CHANGED,
67         POPUP_EVENT,
68         NUM_SIGNALS
69 };
70 static unsigned int signals[NUM_SIGNALS] = { 0 };
71
72 static gboolean selector_popup_menu (GtkWidget *widget);
73
74 G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW)
75
76 /* Selection management.  */
77
78 static GHashTable *
79 create_selected_sources_hash (void)
80 {
81         return g_hash_table_new_full (g_direct_hash, g_direct_equal,
82                                       (GDestroyNotify) g_object_unref, NULL);
83 }
84
85 static ESourceSelectorRebuildData *
86 create_rebuild_data (ESourceSelector *selector)
87 {
88         ESourceSelectorRebuildData *rebuild_data;
89         
90         rebuild_data = g_new0 (ESourceSelectorRebuildData, 1);
91         
92         rebuild_data->selector = selector;
93         rebuild_data->remaining_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
94                                                               (GDestroyNotify) gtk_tree_row_reference_free);
95         rebuild_data->deleted_uids = NULL;
96
97         return rebuild_data;
98 }
99
100
101 static void
102 free_rebuild_data (ESourceSelectorRebuildData *rebuild_data)
103 {
104         GSList *p;
105         
106         g_hash_table_destroy (rebuild_data->remaining_uids);
107         for (p = rebuild_data->deleted_uids; p; p = p->next)
108                 gtk_tree_row_reference_free (p->data);
109         g_slist_free (rebuild_data->deleted_uids);
110         
111         g_free (rebuild_data);
112 }
113
114 static void
115 clear_saved_primary_selection (ESourceSelector *selector)
116 {
117         if (selector->priv->saved_primary_selection != NULL) {
118                 gtk_tree_row_reference_free (selector->priv->saved_primary_selection);
119                 selector->priv->saved_primary_selection = NULL;
120         }
121 }
122
123 static gboolean
124 source_is_selected (ESourceSelector *selector,
125                     ESource *source)
126 {
127         if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL)
128                 return FALSE;
129         else
130                 return TRUE;
131 }
132
133 static void
134 select_source (ESourceSelector *selector,
135                ESource *source)
136 {
137         if (g_hash_table_lookup (selector->priv->selected_sources, source) != NULL)
138                 return;
139
140         g_hash_table_insert (selector->priv->selected_sources, source, source);
141         g_object_ref (source);
142 }
143
144 static void
145 unselect_source (ESourceSelector *selector,
146                  ESource *source)
147 {
148         if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL)
149                 return;
150
151         /* (This will unref the source.)  */
152         g_hash_table_remove (selector->priv->selected_sources, source);
153 }
154
155 static gboolean
156 find_source_iter (ESourceSelector *selector, ESource *source, GtkTreeIter *parent_iter, GtkTreeIter *source_iter)
157 {
158         GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
159
160         if (gtk_tree_model_get_iter_first (model, parent_iter)) {
161                 do {
162                         if (gtk_tree_model_iter_children (model, source_iter, parent_iter)) {
163                                 do {
164                                         void *data;
165
166                                         gtk_tree_model_get (model, source_iter, 0, &data, -1);
167                                         g_assert (E_IS_SOURCE (data));
168
169                                         if (E_SOURCE (data) == source) {
170                                                 g_object_unref (data);
171                                                 
172                                                 return TRUE;
173                                         }
174                                         
175                                         g_object_unref (data);
176                                 } while (gtk_tree_model_iter_next (model, source_iter));
177                         }
178                 } while (gtk_tree_model_iter_next (model, parent_iter));
179         }
180
181         return FALSE;
182 }
183
184 /* Setting up the model.  */
185 static gboolean
186 rebuild_existing_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
187 {
188         ESourceSelectorRebuildData *rebuild_data = data;
189         void *node;
190         const char *uid;
191
192         gtk_tree_model_get (model, iter, 0, &node, -1);
193
194         if (E_IS_SOURCE_GROUP (node)) {
195                 uid = e_source_group_peek_uid (E_SOURCE_GROUP (node));
196
197                 if (e_source_list_peek_group_by_uid (rebuild_data->selector->priv->list, uid)) {
198                         g_hash_table_insert (rebuild_data->remaining_uids, g_strdup (uid), 
199                                              gtk_tree_row_reference_new (model, path));
200                 } else {
201                         rebuild_data->deleted_uids = g_slist_prepend (rebuild_data->deleted_uids, 
202                                                                       gtk_tree_row_reference_new (model, path));
203                 }
204         } else {
205                 uid = e_source_peek_uid (E_SOURCE (node));
206                 if (e_source_list_peek_source_by_uid (rebuild_data->selector->priv->list, uid)) {
207                         g_hash_table_insert (rebuild_data->remaining_uids, g_strdup (uid), 
208                                              gtk_tree_row_reference_new (model, path));
209                 } else {
210                         rebuild_data->deleted_uids = g_slist_prepend (rebuild_data->deleted_uids, 
211                                                                       gtk_tree_row_reference_new (model, path));
212                         
213                         if (g_hash_table_remove (rebuild_data->selector->priv->selected_sources, node))
214                                 rebuild_data->selection_changed = TRUE;
215                 }
216         }
217         
218         g_object_unref (node);
219
220         return FALSE;
221 }
222         
223 static void
224 rebuild_model (ESourceSelector *selector)
225 {
226         ESourceSelectorRebuildData *rebuild_data;
227         GtkTreeStore *tree_store;
228         GtkTreeIter iter;
229         GSList *groups, *p;
230         gboolean set_primary;
231         
232         tree_store = selector->priv->tree_store;
233
234         rebuild_data = create_rebuild_data (selector);
235         set_primary = e_source_selector_peek_primary_selection (selector) != NULL;
236         
237         /* Remove any delete sources or groups */
238         gtk_tree_model_foreach (GTK_TREE_MODEL (tree_store), rebuild_existing_cb, rebuild_data);
239         for (p = rebuild_data->deleted_uids; p; p = p->next) {
240                 GtkTreeRowReference *row_ref = p->data;
241                 GtkTreePath *path;
242                 
243                 path = gtk_tree_row_reference_get_path (row_ref);
244                 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path);
245                 gtk_tree_store_remove (tree_store, &iter);
246
247                 gtk_tree_path_free (path);
248         }
249         
250         /* Add new sources/groups or call row_changed in case they were renamed */
251         groups = e_source_list_peek_groups (selector->priv->list);
252         for (p = groups; p != NULL; p = p->next) {
253                 ESourceGroup *group = E_SOURCE_GROUP (p->data);
254                 GSList *sources, *q;
255                 GtkTreeRowReference *row_ref;
256                 
257                 row_ref = g_hash_table_lookup (rebuild_data->remaining_uids, e_source_group_peek_uid (group));
258                 if (!row_ref) {
259                         gtk_tree_store_append (GTK_TREE_STORE (tree_store), &iter, NULL);
260                         gtk_tree_store_set (GTK_TREE_STORE (tree_store), &iter, 0, group, -1);
261                 } else {
262                         GtkTreePath *path;
263                         
264                         path = gtk_tree_row_reference_get_path (row_ref);
265                         gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path);
266
267                         gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, &iter);
268
269                         gtk_tree_path_free (path);
270                 }
271                 
272                 sources = e_source_group_peek_sources (group);
273                 for (q = sources; q != NULL; q = q->next) {
274                         ESource *source = E_SOURCE (q->data);
275                         GtkTreeIter child_iter;
276
277                         row_ref = g_hash_table_lookup (rebuild_data->remaining_uids, e_source_peek_uid (source));
278                         if (!row_ref) {
279                                 if (selector->priv->select_new) {
280                                         select_source (selector, source);
281                                         rebuild_data->selection_changed = TRUE;
282                                 }
283                                 gtk_tree_store_append (GTK_TREE_STORE (tree_store), &child_iter, &iter);
284                                 gtk_tree_store_set (GTK_TREE_STORE (tree_store), &child_iter, 0, source, -1);
285
286                         } else {
287                                 GtkTreePath *path;
288                                 
289                                 path = gtk_tree_row_reference_get_path (row_ref);
290                                 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &child_iter, path);
291                                 
292                                 gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, &child_iter);
293
294                                 gtk_tree_path_free (path);
295                         }
296                 }
297         }
298
299         if (rebuild_data->selection_changed)
300                 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
301
302         if (set_primary && !e_source_selector_peek_primary_selection (selector))
303                 e_source_selector_set_primary_selection (selector, e_source_list_peek_source_any (selector->priv->list));
304         
305         free_rebuild_data (rebuild_data);
306 }
307
308 static int
309 on_idle_rebuild_model_callback (ESourceSelector *selector)
310 {
311         rebuild_model (selector);
312         selector->priv->rebuild_model_idle_id = 0;
313
314         return FALSE;
315 }
316
317 static void
318 list_changed_callback (ESourceList *list,
319                        ESourceSelector *selector)
320 {
321         ESourceSelectorPrivate *priv = selector->priv;
322
323         if (priv->rebuild_model_idle_id == 0)
324                 priv->rebuild_model_idle_id = g_idle_add ((GSourceFunc) on_idle_rebuild_model_callback,
325                                                           selector);
326 }
327
328 static void
329 setup_model (ESourceSelector *selector)
330 {
331         rebuild_model (selector);
332
333         g_signal_connect_object (selector->priv->list, "changed", G_CALLBACK (list_changed_callback), G_OBJECT (selector), 0);
334 }
335
336
337 /* Data functions for rendering the model.  */
338
339 static void
340 toggle_cell_data_func (GtkTreeViewColumn *column,
341                        GtkCellRenderer *renderer,
342                        GtkTreeModel *model,
343                        GtkTreeIter *iter,
344                        ESourceSelector *selector)
345 {
346         void *data;
347
348         gtk_tree_model_get (model, iter, 0, &data, -1);
349
350         if (E_IS_SOURCE_GROUP (data)) {
351                 g_object_set (renderer, "visible", FALSE, NULL);
352         } else {
353                 g_assert (E_IS_SOURCE (data));
354
355                 g_object_set (renderer, "visible", selector->priv->checkboxes_shown, NULL);
356                 if (source_is_selected (selector, E_SOURCE (data)))
357                         g_object_set (renderer, "active", TRUE, NULL);
358                 else
359                         g_object_set (renderer, "active", FALSE, NULL);
360         }
361
362         g_object_unref (data);
363 }
364
365 static void
366 text_cell_data_func (GtkTreeViewColumn *column,
367                      GtkCellRenderer *renderer,
368                      GtkTreeModel *model,
369                      GtkTreeIter *iter,
370                      ESourceSelector *selector)
371 {
372         void *data;
373
374         gtk_tree_model_get (model, iter, 0, &data, -1);
375
376         if (E_IS_SOURCE_GROUP (data)) {
377                 g_object_set (renderer,
378                               "text", e_source_group_peek_name (E_SOURCE_GROUP (data)),
379                               "weight", PANGO_WEIGHT_BOLD,
380                               "foreground_set", FALSE,
381                               NULL);
382         } else {
383                 ESource *source;
384                 
385                 g_assert (E_IS_SOURCE (data));
386                 source = E_SOURCE (data);
387                 
388                 g_object_set (renderer,
389                               "text", e_source_peek_name (source),
390                               "weight", PANGO_WEIGHT_NORMAL,
391                               "foreground_set", FALSE,
392                               NULL);
393         }
394
395         g_object_unref (data);
396 }
397
398 static void
399 pixbuf_cell_data_func (GtkTreeViewColumn *column,
400                        GtkCellRenderer *renderer,
401                        GtkTreeModel *model,
402                        GtkTreeIter *iter,
403                        ESourceSelector *selector)
404 {
405         void *data;
406
407         gtk_tree_model_get (model, iter, 0, &data, -1);
408
409         if (E_IS_SOURCE_GROUP (data)) {
410                 g_object_set (renderer,
411                               "visible", FALSE,
412                               NULL);
413         } else {        
414                 ESource *source;
415                 GdkPixbuf *pixbuf = NULL;
416                 const gchar *color_spec;
417                 GdkColor color;
418
419                 g_assert (E_IS_SOURCE (data));
420                 source = E_SOURCE (data);
421                 
422                 color_spec = e_source_peek_color_spec (source);
423                 if (color_spec != NULL && gdk_color_parse (color_spec, &color)) {
424                         guint32 rgba;
425
426                         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 16, 16);
427
428                         rgba = (((color.red & 0xff00) << 8) |
429                                 ((color.green & 0xff00)) |
430                                 ((color.blue & 0xff00) >> 8)) << 8;
431
432                         gdk_pixbuf_fill (pixbuf, rgba);
433                 }
434                         
435                 g_object_set (renderer,
436                               "visible", pixbuf != NULL,
437                               "pixbuf", pixbuf,
438                               NULL);
439                         
440                 if (pixbuf)
441                         g_object_unref (pixbuf);
442         }
443
444         g_object_unref (data);
445 }
446
447 /* Custom selection function to make groups non selectable.  */
448 static gboolean
449 selection_func (GtkTreeSelection *selection,
450                 GtkTreeModel *model,
451                 GtkTreePath *path,
452                 gboolean path_currently_selected,
453                 ESourceSelector *selector)
454 {
455         GtkTreeIter iter;
456         void *data;
457
458         if (selector->priv->toggled_last) {
459                 selector->priv->toggled_last = FALSE;
460                 
461                 return FALSE;
462         }               
463
464         if (path_currently_selected)
465                 return TRUE;
466
467         if (! gtk_tree_model_get_iter (model, &iter, path))
468                 return FALSE;
469
470
471         gtk_tree_model_get (model, &iter, 0, &data, -1);
472         if (E_IS_SOURCE_GROUP (data)) {
473                 g_object_unref (data);
474                 
475                 return FALSE;
476         }
477
478         clear_saved_primary_selection (selector);
479         g_object_unref (data);
480
481         return TRUE;
482 }
483
484
485 /* Callbacks.  */
486
487 static void
488 cell_toggled_callback (GtkCellRendererToggle *renderer,
489                        const char *path_string,
490                        ESourceSelector *selector)
491 {
492         GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
493         GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
494         GtkTreeIter iter;
495         ESource *source;
496         void *data;
497
498         if (! gtk_tree_model_get_iter (model, &iter, path)) {
499                 gtk_tree_path_free (path);
500                 return;
501         }
502
503         gtk_tree_model_get (model, &iter, 0, &data, -1);
504         if (!E_IS_SOURCE_GROUP (data)) {
505                 source = E_SOURCE (data);
506
507                 if (source_is_selected (selector, source))
508                         unselect_source (selector, source);
509                 else
510                         select_source (selector, source);
511                 
512                 selector->priv->toggled_last = TRUE;
513                 
514                 gtk_tree_model_row_changed (model, path, &iter);
515                 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
516         }
517
518         gtk_tree_path_free (path);
519         
520         g_object_unref (data);  
521 }
522
523 static void
524 selection_changed_callback (GtkTreeSelection *selection,
525                             ESourceSelector *selector)
526 {
527         g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
528 }
529
530 static gboolean
531 test_collapse_row_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
532 {
533         ESourceSelector *selector = data;
534         ESourceSelectorPrivate *priv;   
535         GtkTreeIter child_iter;
536         
537         priv = selector->priv;
538
539         /* Clear this because something else has been clicked on now */
540         priv->toggled_last = FALSE;
541
542         if (priv->saved_primary_selection)
543                 return FALSE;
544         
545         if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)), NULL, &child_iter))
546                 return FALSE;
547         
548         if (gtk_tree_store_is_ancestor (priv->tree_store, iter, &child_iter)) {
549                 GtkTreePath *child_path;
550                 
551                 child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->tree_store), &child_iter);
552                 priv->saved_primary_selection = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->tree_store), child_path);
553                 gtk_tree_path_free (child_path);
554         }
555         
556         return FALSE;
557 }
558
559 static gboolean
560 row_expanded_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
561 {
562         ESourceSelector *selector = data;
563         ESourceSelectorPrivate *priv;   
564         GtkTreePath *child_path;
565         GtkTreeIter child_iter;
566         
567         priv = selector->priv;
568
569         if (!priv->saved_primary_selection)
570                 return FALSE;
571         
572         child_path = gtk_tree_row_reference_get_path (priv->saved_primary_selection);
573         gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->tree_store), &child_iter, child_path);   
574
575         if (gtk_tree_store_is_ancestor (priv->tree_store, iter, &child_iter)) {
576                 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
577
578                 gtk_tree_selection_select_iter (selection, &child_iter);
579                 clear_saved_primary_selection (selector);
580         }
581
582         gtk_tree_path_free (child_path);
583         
584         return FALSE;   
585 }
586
587 static gboolean
588 selector_popup_menu (GtkWidget *widget)
589 {
590         ESourceSelector *selector = E_SOURCE_SELECTOR (widget);
591         ESource *source;
592         gboolean res = FALSE;
593
594         source = e_source_selector_peek_primary_selection (selector);   
595         g_signal_emit (selector, signals[POPUP_EVENT], 0, source, NULL, &res);
596         return res;
597 }
598
599 static gboolean
600 selector_button_press_event (GtkWidget *widget, GdkEventButton *event, ESourceSelector *selector)
601 {
602         ESourceSelectorPrivate *priv = selector->priv;
603         GtkTreePath *path;
604         ESource *source = NULL;
605         gboolean res = FALSE;
606
607         priv->toggled_last = FALSE;
608         
609         /* only process right-clicks */
610         if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
611                 return FALSE;
612
613         /* Get the source/group */
614         if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), event->x, event->y, &path, NULL, NULL, NULL)) {
615                 GtkTreeIter iter;
616                 gpointer data;
617                 
618                 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->tree_store), &iter, path)) {
619                         gtk_tree_model_get (GTK_TREE_MODEL (priv->tree_store), &iter, 0, &data, -1);
620
621                         /* Do not emit popup since we will not be able to get the source */     
622                         if (E_IS_SOURCE_GROUP (data)) {
623                                 /* do i need to ref it here */
624                                 ESourceGroup *group;
625                                 
626                                 group = E_SOURCE_GROUP (data);g_object_ref (group);
627                                 priv->primary_source_group = group;
628                                 /* data shuld be unreffed after creating the
629                                  * new source*/ 
630                                 return res;
631                         }
632                         
633                         source = E_SOURCE (data);
634                 }
635         }
636
637         if (source)
638                 e_source_selector_set_primary_selection (selector, source);
639
640         g_signal_emit(selector, signals[POPUP_EVENT], 0, source, event, &res);
641
642         if (source)
643                 g_object_unref (source);
644
645         return res;
646 }
647
648 /* GObject methods.  */
649
650 static void
651 e_source_selector_dispose (GObject *object)
652 {
653         ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv;
654
655         if (priv->selected_sources != NULL) {
656                 g_hash_table_destroy (priv->selected_sources);
657                 priv->selected_sources = NULL;
658         }
659
660         if (priv->rebuild_model_idle_id != 0) {
661                 g_source_remove (priv->rebuild_model_idle_id);
662                 priv->rebuild_model_idle_id = 0;
663         }
664
665         if (priv->list != NULL) {
666                 g_object_unref (priv->list);
667                 priv->list = NULL;
668         }
669
670         if (priv->tree_store != NULL) {
671                 g_object_unref (priv->tree_store);
672                 priv->tree_store = NULL;
673         }
674
675         clear_saved_primary_selection (E_SOURCE_SELECTOR (object));
676
677         (* G_OBJECT_CLASS (e_source_selector_parent_class)->dispose) (object);
678 }
679
680 static void
681 e_source_selector_finalize (GObject *object)
682 {
683         ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv;
684
685         g_free (priv);
686
687         (* G_OBJECT_CLASS (e_source_selector_parent_class)->finalize) (object);
688 }
689
690
691 /* Initialization.  */
692 static gboolean
693 ess_bool_accumulator(GSignalInvocationHint *ihint, GValue *out, const GValue *in, void *data)
694 {
695         gboolean val = g_value_get_boolean(in);
696
697         g_value_set_boolean(out, val);
698
699         return !val;
700 }
701
702 static void
703 e_source_selector_class_init (ESourceSelectorClass *class)
704 {
705         GObjectClass *object_class = G_OBJECT_CLASS (class);
706         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
707
708         object_class->dispose  = e_source_selector_dispose;
709         object_class->finalize = e_source_selector_finalize;
710         widget_class->popup_menu = selector_popup_menu;
711
712
713         signals[SELECTION_CHANGED] = 
714                 g_signal_new ("selection_changed",
715                               G_OBJECT_CLASS_TYPE (object_class),
716                               G_SIGNAL_RUN_LAST,
717                               G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed),
718                               NULL, NULL,
719                               g_cclosure_marshal_VOID__VOID,
720                               G_TYPE_NONE, 0);
721
722         signals[PRIMARY_SELECTION_CHANGED] = 
723                 g_signal_new ("primary_selection_changed",
724                               G_OBJECT_CLASS_TYPE (object_class),
725                               G_SIGNAL_RUN_LAST,
726                               G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed),
727                               NULL, NULL,
728                               g_cclosure_marshal_VOID__VOID,
729                               G_TYPE_NONE, 0);
730         signals[POPUP_EVENT] =
731                 g_signal_new ("popup_event",
732                               G_OBJECT_CLASS_TYPE (object_class),
733                               G_SIGNAL_RUN_LAST,
734                               G_STRUCT_OFFSET (ESourceSelectorClass, popup_event),
735                               ess_bool_accumulator, NULL,
736                               e_data_server_ui_marshal_BOOLEAN__OBJECT_BOXED,
737                               G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT,
738                               GDK_TYPE_EVENT|G_SIGNAL_TYPE_STATIC_SCOPE);
739 }
740
741 static gboolean
742 group_search_function   (GtkTreeModel *model,
743                          gint column,
744                          const gchar *key,
745                          GtkTreeIter *iter,
746                          gpointer dummy)
747 {
748         void *data;
749         const char *name = NULL;
750         gboolean status = TRUE;
751         
752         gtk_tree_model_get (model, iter, 0, &data, -1);
753
754         if (E_IS_SOURCE_GROUP (data))
755                 name = e_source_group_peek_name (E_SOURCE_GROUP (data));
756         else {
757                 g_assert (E_IS_SOURCE (data));
758                 
759                 name = e_source_peek_name (E_SOURCE (data));
760         }
761
762         if (name)
763                 status = g_ascii_strncasecmp (name, key, strlen(key)) != 0;
764
765                                                  
766         g_object_unref (data);
767         
768         return status;
769 }
770
771 static void
772 e_source_selector_init (ESourceSelector *selector)
773 {
774         ESourceSelectorPrivate *priv;
775         GtkTreeViewColumn *column;
776         GtkCellRenderer *cell_renderer;
777         GtkTreeSelection *selection;
778
779         priv = g_new0 (ESourceSelectorPrivate, 1);
780         selector->priv = priv;
781         
782         gtk_tree_view_set_search_column (GTK_TREE_VIEW (selector), 0);
783         gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (selector), group_search_function, NULL, NULL);
784         gtk_tree_view_set_enable_search (GTK_TREE_VIEW (selector), TRUE);
785         
786         g_signal_connect (G_OBJECT (selector), "button_press_event",
787                           G_CALLBACK (selector_button_press_event), selector);
788
789         priv->toggled_last = FALSE;
790         priv->checkboxes_shown = TRUE;
791         priv->select_new = FALSE;
792
793         priv->selected_sources = create_selected_sources_hash ();
794
795         priv->tree_store = gtk_tree_store_new (1, G_TYPE_OBJECT);
796         gtk_tree_view_set_model (GTK_TREE_VIEW (selector), GTK_TREE_MODEL (priv->tree_store));
797
798         column = gtk_tree_view_column_new ();
799         gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column);
800
801         cell_renderer = gtk_cell_renderer_pixbuf_new ();
802         g_object_set (G_OBJECT (cell_renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
803         gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
804         gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) pixbuf_cell_data_func, selector, NULL);
805         cell_renderer = gtk_cell_renderer_toggle_new ();
806         gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
807         gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) toggle_cell_data_func, selector, NULL);
808         g_signal_connect (cell_renderer, "toggled", G_CALLBACK (cell_toggled_callback), selector);
809
810         cell_renderer = gtk_cell_renderer_text_new ();
811         g_object_set (G_OBJECT (cell_renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
812         g_object_set (G_OBJECT (cell_renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
813         gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
814         gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) text_cell_data_func, selector, NULL);
815
816         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
817         gtk_tree_selection_set_select_function (selection, (GtkTreeSelectionFunc) selection_func, selector, NULL);
818         g_signal_connect_object (selection, "changed", G_CALLBACK (selection_changed_callback), G_OBJECT (selector), 0);
819
820         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (selector), FALSE);
821
822         g_signal_connect (G_OBJECT (selector), "test-collapse-row", G_CALLBACK (test_collapse_row_callback), selector);
823         g_signal_connect (G_OBJECT (selector), "row-expanded", G_CALLBACK (row_expanded_callback), selector);
824 }
825
826
827 /* Public API.  */
828
829 /**
830  * e_source_selector_new:
831  * @list: A source list.
832  * 
833  * Create a new view for @list.  The view will update automatically when @list
834  * changes.
835  * 
836  * Return value: The newly created widget.
837  **/
838 GtkWidget *
839 e_source_selector_new (ESourceList *list)
840 {
841         ESourceSelector *selector;
842
843         g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
844
845         selector = g_object_new (e_source_selector_get_type (), NULL);
846
847         selector->priv->list = list;
848         g_object_ref (list);
849
850         setup_model (selector);
851
852         gtk_tree_view_expand_all (GTK_TREE_VIEW (selector));
853
854         return GTK_WIDGET (selector);
855 }
856
857
858 /**
859  * e_source_selector_get_selection:
860  * @selector: an #ESourceSelector
861  * 
862  * Get the list of selected sources, i.e. those that were enabled through the
863  * corresponding checkboxes in the tree.
864  * 
865  * Return value: A list of the ESources currently selected.  The sources will
866  * be in the same order as they appear on the screen, and the list should be
867  * freed using e_source_selector_free_selection().
868  **/
869 GSList *
870 e_source_selector_get_selection (ESourceSelector *selector)
871 {
872         GSList *selection_list;
873         GSList *groups;
874         GSList *p;
875
876         g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
877
878         selection_list = NULL;
879
880         groups = e_source_list_peek_groups (selector->priv->list);
881         for (p = groups; p != NULL; p = p->next) {
882                 ESourceGroup *group = E_SOURCE_GROUP (p->data);
883                 GSList *sources;
884                 GSList *q;
885
886                 sources = e_source_group_peek_sources (group);
887                 for (q = sources; q != NULL; q = q->next) {
888                         ESource *source = E_SOURCE (q->data);
889
890                         if (source_is_selected (selector, source)) {
891                                 selection_list = g_slist_prepend (selection_list, source);
892                                 g_object_ref (source);
893                         }
894                 }
895         }
896
897         return g_slist_reverse (selection_list);
898 }
899
900 /**
901  * e_source_selector_get_primary_source_group:
902  * @selector: an #ESourceSelector
903  * 
904  * Gets the primary source group associated with the selector.
905  *
906  * Return value: primary_source_group if selector is valid, NULL otherwise.
907  **/
908 ESourceGroup *
909 e_source_selector_get_primary_source_group (ESourceSelector *selector)
910 {
911         g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
912         
913         return selector->priv->primary_source_group;
914
915 }
916
917 /**
918  * e_source_list_free_selection:
919  * @list: A selection list returned by e_source_selector_get_selection().
920  * 
921  * Free the selection list.
922  **/
923 void
924 e_source_selector_free_selection (GSList *list)
925 {
926         g_slist_foreach (list, (GFunc) g_object_unref, NULL);
927         g_slist_free (list);
928 }
929
930
931 /**
932  * e_source_selector_show_selection:
933  * @selector: An ESourceSelector widget
934  * 
935  * Specify whether the checkboxes in the ESourceSelector should be shown or
936  * not.
937  **/
938 void
939 e_source_selector_show_selection (ESourceSelector *selector,
940                                   gboolean show)
941 {
942         g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
943
944         show = !! show;
945         if (show == selector->priv->checkboxes_shown)
946                 return;
947
948         selector->priv->checkboxes_shown = show;
949
950         gtk_tree_model_foreach (GTK_TREE_MODEL (selector->priv->tree_store),
951                                 (GtkTreeModelForeachFunc) gtk_tree_model_row_changed,
952                                 NULL);
953 }
954
955 /**
956  * e_source_selector_selection_shown:
957  * @selector: an #ESourceSelector
958  * 
959  * Check whether the checkboxes in the ESourceSelector are being shown or not.
960  * 
961  * Return value: %TRUE if the checkboxes are shown, %FALSE otherwise.
962  **/
963 gboolean
964 e_source_selector_selection_shown (ESourceSelector *selector)
965 {
966         g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
967
968         return selector->priv->checkboxes_shown;
969 }
970
971 /**
972  * e_source_selector_set_select_new:
973  * @selector: An #ESourceSelector widget
974  * @state: A gboolean
975  *
976  * Set whether or not to select new sources added to @selector.
977  **/
978 void
979 e_source_selector_set_select_new (ESourceSelector *selector, gboolean state)
980 {
981         g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
982
983         selector->priv->select_new = state;
984 }
985
986 /**
987  * e_source_selector_select_source:
988  * @selector: An #ESourceSelector widget
989  * @source: An #ESource.
990  * 
991  * Select @source in @selector.
992  **/
993 void
994 e_source_selector_select_source (ESourceSelector *selector,
995                                  ESource *source)
996 {
997         GtkTreeIter parent_iter, source_iter;
998         
999         g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1000         g_return_if_fail (E_IS_SOURCE (source));
1001
1002         if (source_is_selected (selector, source))
1003                 return;
1004
1005         select_source (selector, source);
1006
1007         if (find_source_iter (selector, source, &parent_iter, &source_iter)) {
1008                 GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
1009                 GtkTreePath *path;
1010                 
1011                 path = gtk_tree_model_get_path (model, &source_iter);
1012                 gtk_tree_model_row_changed (model, path, &source_iter);
1013                 gtk_tree_path_free (path);
1014                 
1015                 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1016         }       
1017 }
1018
1019 /**
1020  * e_source_selector_unselect_source:
1021  * @selector: An #ESourceSelector widget
1022  * @source: An #ESource.
1023  * 
1024  * Unselect @source in @selector.
1025  **/
1026 void
1027 e_source_selector_unselect_source (ESourceSelector *selector,
1028                                    ESource *source)
1029 {
1030         GtkTreeIter parent_iter, source_iter;
1031
1032         g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1033         g_return_if_fail (E_IS_SOURCE (source));
1034
1035         if (! source_is_selected (selector, source))
1036                 return;
1037
1038         unselect_source (selector, source);
1039
1040         if (find_source_iter (selector, source, &parent_iter, &source_iter)) {
1041                 GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
1042                 GtkTreePath *path;
1043                 
1044                 path = gtk_tree_model_get_path (model, &source_iter);
1045                 gtk_tree_model_row_changed (model, path, &source_iter);
1046                 gtk_tree_path_free (path);
1047                 
1048                 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1049         }
1050 }
1051
1052 /**
1053  * e_source_selector_source_is_selected:
1054  * @selector: An #ESourceSelector widget
1055  * @source: An #ESource.
1056  * 
1057  * Check whether @source is selected in @selector.
1058  * 
1059  * Return value: %TRUE if @source is currently selected, %FALSE otherwise.
1060  **/
1061 gboolean
1062 e_source_selector_source_is_selected (ESourceSelector *selector,
1063                                       ESource *source)
1064 {
1065         g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
1066         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1067
1068         return source_is_selected (selector, source);
1069 }
1070
1071 /**
1072  * e_source_selector_peek_primary_selection:
1073  * @selector: An #ESourceSelector widget
1074  * 
1075  * Get the primary selected source.  The primary selection is the one that is
1076  * highlighted through the normal #GtkTreeView selection mechanism (as opposed
1077  * to the "normal" selection, which is the set of source whose checkboxes are
1078  * checked).
1079  * 
1080  * Return value: The selected source.
1081  **/
1082 ESource *
1083 e_source_selector_peek_primary_selection (ESourceSelector *selector)
1084 {
1085         GtkTreeModel *model;
1086         GtkTreeIter iter;
1087         gboolean have_iter = FALSE;
1088         void *data = NULL;
1089
1090         g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
1091
1092         model = GTK_TREE_MODEL (selector->priv->tree_store);
1093
1094         if (selector->priv->saved_primary_selection) {
1095                 GtkTreePath *child_path;
1096                 
1097                 child_path = gtk_tree_row_reference_get_path (selector->priv->saved_primary_selection);
1098                 if (child_path) {
1099                         if (gtk_tree_model_get_iter (GTK_TREE_MODEL (selector->priv->tree_store), &iter, child_path))
1100                                 have_iter = TRUE;
1101                         gtk_tree_path_free (child_path);
1102                 }
1103         }
1104
1105         if (!have_iter && ! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)), NULL, &iter))
1106                 return NULL;
1107
1108         gtk_tree_model_get (model, &iter, 0, &data, -1);
1109         if (!data)
1110                 return NULL;
1111
1112         if (! E_IS_SOURCE (data)) {
1113                 g_object_unref (data);
1114                 
1115                 return NULL;
1116         }
1117         
1118         g_object_unref (data);
1119         
1120         return E_SOURCE (data);
1121 }
1122
1123 /**
1124  * e_source_selector_set_primary_selection:
1125  * @selector: an #ESourceSelector widget
1126  * @source: an #ESource to select
1127  * 
1128  * Set the primary selected source.
1129  **/
1130 void
1131 e_source_selector_set_primary_selection (ESourceSelector *selector, ESource *source)
1132 {
1133         ESourceSelectorPrivate *priv;
1134         GtkTreeIter parent_iter, source_iter;
1135
1136         g_return_if_fail (selector != NULL);
1137         g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1138         g_return_if_fail (source != NULL);
1139         g_return_if_fail (E_IS_SOURCE (source));
1140
1141         priv = selector->priv;
1142         
1143         if (find_source_iter (selector, source, &parent_iter, &source_iter)) {
1144                 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
1145                 GtkTreePath *path;
1146                 
1147                 /* We block the signal because this all needs to be atomic */
1148                 g_signal_handlers_block_matched (selection, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, selection_changed_callback, NULL);
1149                 gtk_tree_selection_unselect_all (selection);
1150                 g_signal_handlers_unblock_matched (selection, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, selection_changed_callback, NULL);
1151
1152                 clear_saved_primary_selection (selector);
1153                 
1154                 path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->tree_store), &parent_iter);
1155                 
1156                 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (selector), path)) {
1157                         gtk_tree_selection_select_iter (selection, &source_iter);
1158                 } else {
1159                         GtkTreePath *child_path;
1160                         
1161                         child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->tree_store), &source_iter);                 
1162                         priv->saved_primary_selection = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->tree_store), child_path);
1163                         gtk_tree_path_free (child_path);
1164
1165                         /* We do this by hand because we aren't changing the tree selection */
1166                         if (!source_is_selected (selector, source)) {
1167                                 select_source (selector, source);
1168                                 gtk_tree_model_row_changed (GTK_TREE_MODEL (priv->tree_store), path, &source_iter);
1169                                 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1170                         }
1171                         
1172                         g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
1173                 }
1174                 
1175                 gtk_tree_path_free (path);
1176         } else {
1177                 g_warning (G_STRLOC ": Cannot find source %p (%s) in selector %p", 
1178                            source, e_source_peek_name (source), selector);
1179         }
1180 }
1181