source: trunk/third/evolution/shell/e-storage-set-view.c @ 18157

Revision 18157, 65.0 KB checked in by ghudson, 22 years ago (diff)
Fix up mismerges.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* e-storage-set-view.c
3 *
4 * Copyright (C) 2000, 2001, 2002 Ximian, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Author: Ettore Perazzoli
21 * Etree-ification: Chris Toshok
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include "e-storage-set-view.h"
29
30#include "e-util/e-gtk-utils.h"
31
32#include "e-corba-storage.h"
33#include "e-icon-factory.h"
34#include "e-folder-dnd-bridge.h"
35#include "e-shell-constants.h"
36
37#include <gal/util/e-util.h>
38#include <gal/widgets/e-gui-utils.h>
39#include <gal/e-table/e-tree-memory-callbacks.h>
40#include <gal/e-table/e-cell-text.h>
41#include <gal/e-table/e-cell-toggle.h>
42#include <gal/e-table/e-cell-tree.h>
43#include <gal/unicode/gunicode.h>
44
45#include <glib.h>
46#include <gnome.h>
47#include <libgnome/gnome-defs.h>
48#include <libgnome/gnome-util.h>
49#include <bonobo/bonobo-ui-util.h>
50#include <libgnome/gnome-util.h>
51
52#include "check-empty.xpm"
53#include "check-filled.xpm"
54#include "check-missing.xpm"
55
56
57static GdkPixbuf *checks [3];
58
59
60/*#define DEBUG_XML*/
61
62#define ROOT_NODE_NAME "/RootNode"
63
64
65/* This is used on the source side to define the two basic types that we always
66   export.  */
67enum _DndTargetTypeIdx {
68        E_FOLDER_DND_PATH_TARGET_TYPE_IDX = 0,
69        E_SHORTCUT_TARGET_TYPE_IDX = 1
70};
71typedef enum _DndTargetTypeIdx DndTargetTypeIdx;
72
73#define E_SHORTCUT_TARGET_TYPE     "E-SHORTCUT"
74
75
76#define PARENT_TYPE E_TREE_TYPE
77static ETreeClass *parent_class = NULL;
78
79struct _EStorageSetViewPrivate {
80        EStorageSet *storage_set;
81
82        BonoboUIComponent *ui_component;
83        BonoboUIContainer *ui_container;
84
85        ETreeModel *etree_model;
86        ETreePath root_node;
87
88        GHashTable *path_to_etree_node;
89
90        GHashTable *type_name_to_pixbuf;
91
92        /* Path of the row selected by the latest "cursor_activated" signal.  */
93        char *selected_row_path;
94
95        /* Path of the row selected by a right click.  */
96        char *right_click_row_path;
97
98        unsigned int show_folders : 1;
99        unsigned int show_checkboxes : 1;
100        unsigned int allow_dnd : 1;
101        unsigned int search_enabled : 1;
102
103        /* The `Evolution::ShellComponentDnd::SourceFolder' interface for the
104           folder we are dragging from, or CORBA_OBJECT_NIL if no dragging is
105           happening.  */
106        GNOME_Evolution_ShellComponentDnd_SourceFolder drag_corba_source_interface;
107
108        /* Source context information.  NULL if no dragging is in progress.  */
109        GNOME_Evolution_ShellComponentDnd_SourceFolder_Context *drag_corba_source_context;
110
111        /* The data.  */
112        GNOME_Evolution_ShellComponentDnd_Data *drag_corba_data;
113
114        GHashTable *checkboxes;
115
116        /* Callback to determine whether the row should have a checkbox or
117           not, when show_checkboxes is TRUE.  */
118        EStorageSetViewHasCheckBoxFunc has_checkbox_func;
119        void *has_checkbox_func_data;
120};
121
122
123enum {
124        FOLDER_SELECTED,
125        FOLDER_OPENED,
126        DND_ACTION,
127        FOLDER_CONTEXT_MENU_POPPING_UP,
128        FOLDER_CONTEXT_MENU_POPPED_DOWN,
129        CHECKBOXES_CHANGED,
130        LAST_SIGNAL
131};
132
133static unsigned int signals[LAST_SIGNAL] = { 0 };
134
135
136/* Forward declarations.  */
137
138static void setup_folder_changed_callbacks (EStorageSetView *storage_set_view,
139                                            EFolder *folder,
140                                            const char *path);
141
142
143/* DND stuff.  */
144
145enum _DndTargetType {
146        DND_TARGET_TYPE_URI_LIST,
147        DND_TARGET_TYPE_E_SHORTCUT
148};
149typedef enum _DndTargetType DndTargetType;
150
151#define URI_LIST_TYPE   "text/uri-list"
152#define E_SHORTCUT_TYPE "E-SHORTCUT"
153
154
155/* Sorting callbacks.  */
156
157static int
158storage_sort_callback (ETreeMemory *etmm,
159                       ETreePath node1,
160                       ETreePath node2,
161                       void *closure)
162{
163        char *folder_path_1;
164        char *folder_path_2;
165        gboolean path_1_local;
166        gboolean path_2_local;
167
168        folder_path_1 = e_tree_memory_node_get_data(etmm, node1);
169        folder_path_2 = e_tree_memory_node_get_data(etmm, node2);
170
171        /* FIXME bad hack to put the "my evolution" and "local" storages on
172           top.  */
173
174        if (strcmp (folder_path_1, E_PATH_SEPARATOR_S E_SUMMARY_STORAGE_NAME) == 0)
175                return -1;
176        if (strcmp (folder_path_2, E_PATH_SEPARATOR_S E_SUMMARY_STORAGE_NAME) == 0)
177                return +1;
178       
179        path_1_local = ! strcmp (folder_path_1, E_PATH_SEPARATOR_S E_LOCAL_STORAGE_NAME);
180        path_2_local = ! strcmp (folder_path_2, E_PATH_SEPARATOR_S E_LOCAL_STORAGE_NAME);
181
182        if (path_1_local && path_2_local)
183                return 0;
184        if (path_1_local)
185                return 1;
186        if (path_2_local)
187                return -1;
188       
189        return g_utf8_collate (e_tree_model_value_at (E_TREE_MODEL (etmm), node1, 0),
190                               e_tree_model_value_at (E_TREE_MODEL (etmm), node2, 0));
191}
192
193static int
194folder_sort_callback (ETreeMemory *etmm,
195                      ETreePath node1,
196                      ETreePath node2,
197                      void *closure)
198{
199        EStorageSetViewPrivate *priv;
200        EFolder *folder_1, *folder_2;
201        const char *folder_path_1, *folder_path_2;
202        int priority_1, priority_2;
203
204        priv = E_STORAGE_SET_VIEW (closure)->priv;
205
206        folder_path_1 = e_tree_memory_node_get_data(etmm, node1);
207        folder_path_2 = e_tree_memory_node_get_data(etmm, node2);
208
209        folder_1 = e_storage_set_get_folder (priv->storage_set, folder_path_1);
210        folder_2 = e_storage_set_get_folder (priv->storage_set, folder_path_2);
211
212        priority_1 = e_folder_get_sorting_priority (folder_1);
213        priority_2 = e_folder_get_sorting_priority (folder_2);
214
215        if (priority_1 == priority_2)
216                return g_utf8_collate (e_tree_model_value_at (E_TREE_MODEL (etmm), node1, 0),
217                                       e_tree_model_value_at (E_TREE_MODEL (etmm), node2, 0));
218        else if (priority_1 < priority_2)
219                return -1;
220        else                    /* priority_1 > priority_2 */
221                return +1;
222}
223
224
225/* Helper functions.  */
226
227static gboolean
228add_node_to_hash (EStorageSetView *storage_set_view,
229                  const char *path,
230                  ETreePath node)
231{
232        EStorageSetViewPrivate *priv;
233        char *hash_path;
234
235        g_return_val_if_fail (g_path_is_absolute (path), FALSE);
236
237        priv = storage_set_view->priv;
238
239        if (g_hash_table_lookup (priv->path_to_etree_node, path) != NULL) {
240                g_warning ("EStorageSetView: Node already existing while adding -- %s", path);
241                return FALSE;
242        }
243
244        hash_path = g_strdup (path);
245
246        g_hash_table_insert (priv->path_to_etree_node, hash_path, node);
247
248        return TRUE;
249}
250
251static ETreePath
252lookup_node_in_hash (EStorageSetView *storage_set_view,
253                     const char *path)
254{
255        EStorageSetViewPrivate *priv;
256        ETreePath node;
257
258        priv = storage_set_view->priv;
259
260        node = g_hash_table_lookup (priv->path_to_etree_node, path);
261        if (node == NULL)
262                g_warning ("EStorageSetView: Node not found while updating -- %s", path);
263
264        return node;
265}
266
267static ETreePath
268remove_node_from_hash (EStorageSetView *storage_set_view,
269                       const char *path)
270{
271        EStorageSetViewPrivate *priv;
272        ETreePath node;
273
274        priv = storage_set_view->priv;
275
276        node = g_hash_table_lookup (priv->path_to_etree_node, path);
277        if (node == NULL) {
278                g_warning ("EStorageSetView: Node not found while removing -- %s", path);
279                return NULL;
280        }
281
282        g_hash_table_remove (priv->path_to_etree_node, path);
283
284        return node;
285}
286
287static GdkPixbuf *
288get_pixbuf_for_folder (EStorageSetView *storage_set_view,
289                       EFolder *folder)
290{
291        const char *type_name;
292        EStorageSetViewPrivate *priv;
293        EFolderTypeRegistry *folder_type_registry;
294        EStorageSet *storage_set;
295        GdkPixbuf *icon_pixbuf;
296        GdkPixbuf *scaled_pixbuf;
297        const char *custom_icon_name;
298        int icon_pixbuf_width, icon_pixbuf_height;
299
300        priv = storage_set_view->priv;
301
302        custom_icon_name = e_folder_get_custom_icon_name (folder);
303        if (custom_icon_name != NULL)
304                return e_icon_factory_get_icon (custom_icon_name, TRUE);
305
306        type_name = e_folder_get_type_string (folder);
307
308        scaled_pixbuf = g_hash_table_lookup (priv->type_name_to_pixbuf, type_name);
309        if (scaled_pixbuf != NULL)
310                return scaled_pixbuf;
311
312        storage_set = priv->storage_set;
313        folder_type_registry = e_storage_set_get_folder_type_registry (storage_set);
314
315        icon_pixbuf = e_folder_type_registry_get_icon_for_type (folder_type_registry,
316                                                                type_name, TRUE);
317
318        if (icon_pixbuf == NULL)
319                return NULL;
320
321        icon_pixbuf_width = gdk_pixbuf_get_width (icon_pixbuf);
322        icon_pixbuf_height = gdk_pixbuf_get_height (icon_pixbuf);
323
324        if (icon_pixbuf_width == E_SHELL_MINI_ICON_SIZE && icon_pixbuf_height == E_SHELL_MINI_ICON_SIZE) {
325                scaled_pixbuf = gdk_pixbuf_ref (icon_pixbuf);
326        } else {
327                scaled_pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (icon_pixbuf),
328                                                gdk_pixbuf_get_has_alpha (icon_pixbuf),
329                                                gdk_pixbuf_get_bits_per_sample (icon_pixbuf),
330                                                E_SHELL_MINI_ICON_SIZE, E_SHELL_MINI_ICON_SIZE);
331
332                gdk_pixbuf_scale (icon_pixbuf, scaled_pixbuf,
333                                  0, 0, E_SHELL_MINI_ICON_SIZE, E_SHELL_MINI_ICON_SIZE,
334                                  0.0, 0.0,
335                                  (double) E_SHELL_MINI_ICON_SIZE / gdk_pixbuf_get_width (icon_pixbuf),
336                                  (double) E_SHELL_MINI_ICON_SIZE / gdk_pixbuf_get_height (icon_pixbuf),
337                                  GDK_INTERP_HYPER);
338        }
339
340        g_hash_table_insert (priv->type_name_to_pixbuf, g_strdup (type_name), scaled_pixbuf);
341
342        return scaled_pixbuf;
343}
344
345static EFolder *
346get_folder_at_node (EStorageSetView *storage_set_view,
347                    ETreePath path)
348{
349        EStorageSetViewPrivate *priv;
350        const char *folder_path;
351
352        priv = storage_set_view->priv;
353
354        if (path == NULL)
355                return NULL;
356
357        folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path);
358        g_assert (folder_path != NULL);
359
360        return e_storage_set_get_folder (priv->storage_set, folder_path);
361}
362
363static EvolutionShellComponentClient *
364get_component_at_node (EStorageSetView *storage_set_view,
365                       ETreePath path)
366{
367        EStorageSetViewPrivate *priv;
368        EvolutionShellComponentClient *component_client;
369        EFolderTypeRegistry *folder_type_registry;
370        EFolder *folder;
371
372        priv = storage_set_view->priv;
373
374        folder = get_folder_at_node (storage_set_view, path);
375        if (folder == NULL)
376                return NULL;
377
378        folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
379        g_assert (folder_type_registry != NULL);
380
381        component_client = e_folder_type_registry_get_handler_for_type (folder_type_registry,
382                                                                        e_folder_get_type_string (folder));
383
384        return component_client;
385}
386
387static GNOME_Evolution_ShellComponentDnd_ActionSet
388convert_gdk_drag_action_set_to_corba (GdkDragAction action)
389{
390        GNOME_Evolution_ShellComponentDnd_Action retval;
391
392        retval = GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT;
393
394        if (action & GDK_ACTION_COPY)
395                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_COPY;
396        if (action & GDK_ACTION_MOVE)
397                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_MOVE;
398        if (action & GDK_ACTION_LINK)
399                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_LINK;
400        if (action & GDK_ACTION_ASK)
401                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_ASK;
402
403        return retval;
404}
405
406
407/* The weakref callback for priv->ui_component.  */
408
409static void
410ui_container_destroy_notify (void *data)
411{
412        EStorageSetViewPrivate *priv  = (EStorageSetViewPrivate *) data;
413
414        priv->ui_container = NULL;
415}       
416
417
418/* Custom marshalling function.  */
419
420typedef void (* GtkSignal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING) (GtkObject *object,
421                                                                      GdkDragContext *action,
422                                                                      const char *,
423                                                                      const char *,
424                                                                      const char *);
425
426static void
427marshal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING (GtkObject *object,
428                                                   GtkSignalFunc func,
429                                                   void *func_data,
430                                                   GtkArg *args)
431{
432        GtkSignal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING rfunc;
433
434        rfunc = (GtkSignal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING) func;
435        (* rfunc) (object,
436                   GTK_VALUE_POINTER (args[0]),
437                   GTK_VALUE_STRING (args[1]),
438                   GTK_VALUE_STRING (args[2]),
439                   GTK_VALUE_STRING (args[3]));
440}
441
442
443/* DnD selection setup stuff.  */
444
445/* This will create an array of GtkTargetEntries from the specified list of DND
446   types.  The type name will *not* be allocated in the list, as this is
447   supposed to be used only temporarily to set up the cell as a drag source.  */
448static GtkTargetEntry *
449create_target_entries_from_dnd_type_list (GList *dnd_types,
450                                          int *num_entries_return)
451{
452        GtkTargetEntry *entries;
453        GList *p;
454        int num_entries;
455        int i;
456
457        if (dnd_types == NULL)
458                num_entries = 0;
459        else
460                num_entries = g_list_length (dnd_types);
461
462        /* We always add two entries, one for an Evolution URI type, and one
463           for e-shortcuts.  This will let us do drag & drop within Evolution
464           at least.  */
465        num_entries += 2;
466
467        entries = g_new (GtkTargetEntry, num_entries);
468
469        i = 0;
470
471        /* The Evolution URI will always come first.  */
472        entries[i].target = E_FOLDER_DND_PATH_TARGET_TYPE;
473        entries[i].flags = 0;
474        entries[i].info = i;
475        g_assert (i == E_FOLDER_DND_PATH_TARGET_TYPE_IDX);
476        i ++;
477
478        /* ...Then the shortcut type.  */
479        entries[i].target = E_SHORTCUT_TARGET_TYPE;
480        entries[i].flags = 0;
481        entries[i].info = i;
482        g_assert (i == E_SHORTCUT_TARGET_TYPE_IDX);
483        i ++;
484
485        for (p = dnd_types; p != NULL; p = p->next, i++) {
486                const char *dnd_type;
487
488                g_assert (i < num_entries);
489
490                dnd_type = (const char *) p->data;
491
492                entries[i].target = (char *) dnd_type;
493                entries[i].flags  = 0;
494                entries[i].info   = i;
495        }
496
497        *num_entries_return = num_entries;
498        return entries;
499}
500
501static void
502free_target_entries (GtkTargetEntry *entries)
503{
504        g_assert (entries != NULL);
505
506        /* The target names are not strdup()ed so a simple free will do.  */
507        g_free (entries);
508}
509
510static GtkTargetList *
511create_target_list_for_node (EStorageSetView *storage_set_view,
512                             ETreePath node)
513{
514        EStorageSetViewPrivate *priv;
515        GtkTargetList *target_list;
516        EFolderTypeRegistry *folder_type_registry;
517        GList *exported_dnd_types;
518        GtkTargetEntry *target_entries;
519        EFolder *folder;
520        const char *folder_type;
521        int num_target_entries;
522
523        priv = storage_set_view->priv;
524
525        folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
526
527        folder = get_folder_at_node (storage_set_view, node);
528        folder_type = e_folder_get_type_string (folder);
529
530        exported_dnd_types = e_folder_type_registry_get_exported_dnd_types_for_type (folder_type_registry,
531                                                                                     folder_type);
532
533        target_entries = create_target_entries_from_dnd_type_list (exported_dnd_types,
534                                                                   &num_target_entries);
535        g_assert (target_entries != NULL);
536
537        target_list = gtk_target_list_new (target_entries, num_target_entries);
538
539        free_target_entries (target_entries);
540
541        return target_list;
542}
543
544static void
545set_e_shortcut_selection (EStorageSetView *storage_set_view,
546                          GtkSelectionData *selection_data)
547{
548        EStorageSetViewPrivate *priv;
549        ETreePath node;
550        EFolder *folder;
551        int shortcut_len;
552        char *shortcut;
553        const char *name;
554        const char *folder_path;
555
556        g_assert (storage_set_view != NULL);
557        g_assert (selection_data != NULL);
558
559        priv = storage_set_view->priv;
560
561        node = lookup_node_in_hash (storage_set_view, priv->selected_row_path);
562
563        folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), node);
564        g_assert (folder_path != NULL);
565
566        folder = e_storage_set_get_folder (priv->storage_set, folder_path);
567        if (folder != NULL)
568                name = e_folder_get_name (folder);
569        else
570                name = NULL;
571
572        /* FIXME: Get `evolution:' from somewhere instead of hardcoding it here.  */
573
574        if (name != NULL)
575                shortcut_len = strlen (name);
576        else
577                shortcut_len = 0;
578       
579        shortcut_len ++;        /* Separating zero.  */
580
581        shortcut_len += strlen ("evolution:");
582        shortcut_len += strlen (priv->selected_row_path);
583        shortcut_len ++;        /* Trailing zero.  */
584
585        shortcut = g_malloc (shortcut_len);
586
587        if (name == NULL)
588                sprintf (shortcut, "%cevolution:%s", '\0', priv->selected_row_path);
589        else
590                sprintf (shortcut, "%s%cevolution:%s", name, '\0', priv->selected_row_path);
591
592        gtk_selection_data_set (selection_data, selection_data->target,
593                                8, (guchar *) shortcut, shortcut_len);
594
595        g_free (shortcut);
596}
597
598static void
599set_evolution_path_selection (EStorageSetView *storage_set_view,
600                              GtkSelectionData *selection_data)
601{
602        EStorageSetViewPrivate *priv;
603
604        g_assert (storage_set_view != NULL);
605        g_assert (selection_data != NULL);
606
607        priv = storage_set_view->priv;
608
609        gtk_selection_data_set (selection_data, selection_data->target,
610                                8, (guchar *) priv->selected_row_path, strlen (priv->selected_row_path) + 1);
611}
612
613
614/* Folder context menu.  */
615
616struct _FolderPropertyItemsData {
617        EStorageSetView *storage_set_view;
618        ECorbaStorage *corba_storage;
619        int num_items;
620};
621typedef struct _FolderPropertyItemsData FolderPropertyItemsData;
622
623static void
624folder_property_item_verb_callback    (BonoboUIComponent *component,
625                                       void *user_data,
626                                       const char *cname)
627{
628        FolderPropertyItemsData *data;
629        GtkWidget *toplevel_widget;
630        const char *p, *path;
631        int item_number;
632
633        data = (FolderPropertyItemsData *) user_data;
634
635        p = strrchr (cname, ':');
636        g_assert (p != NULL);
637
638        item_number = atoi (p + 1) - 1;
639        g_assert (item_number >= 0);
640
641        toplevel_widget = gtk_widget_get_toplevel (GTK_WIDGET (data->storage_set_view));
642
643        path = strchr (data->storage_set_view->priv->right_click_row_path + 1, E_PATH_SEPARATOR);
644        if (path == NULL)
645                path = "/";
646        e_corba_storage_show_folder_properties (data->corba_storage, path,
647                                                item_number, toplevel_widget->window);
648}
649
650static FolderPropertyItemsData *
651setup_folder_properties_items_if_corba_storage_clicked (EStorageSetView *storage_set_view)
652{
653        EStorageSetViewPrivate *priv;
654        EStorage *storage;
655        GSList *items, *p;
656        GString *xml;
657        FolderPropertyItemsData *data;
658        const char *slash;
659        char *storage_name;
660        int num_property_items;
661        int i;
662
663        priv = storage_set_view->priv;
664
665        slash = strchr (priv->right_click_row_path + 1, E_PATH_SEPARATOR);
666        if (slash == NULL)
667                storage_name = g_strdup (priv->right_click_row_path + 1);
668
669        else
670                storage_name = g_strndup (priv->right_click_row_path + 1,
671                                          slash - (priv->right_click_row_path + 1));
672
673        storage = e_storage_set_get_storage (priv->storage_set, storage_name);
674        g_free (storage_name);
675
676        if (storage == NULL || ! E_IS_CORBA_STORAGE (storage))
677                return 0;
678
679        items = e_corba_storage_get_folder_property_items (E_CORBA_STORAGE (storage));
680        if (items == NULL)
681                return 0;
682
683        xml = g_string_new ("<placeholder name=\"StorageFolderPropertiesPlaceholder\">");
684        g_string_append (xml, "<separator f=\"\" name=\"EStorageSetViewFolderPropertiesSeparator\"/>");
685
686        num_property_items = 0;
687        for (p = items; p != NULL; p = p->next) {
688                const ECorbaStoragePropertyItem *item;
689                char *encoded_label;
690                char *encoded_tooltip;
691
692                item = (const ECorbaStoragePropertyItem *) p->data;
693                num_property_items ++;
694
695                g_string_sprintfa (xml, "<menuitem name=\"EStorageSetView:FolderPropertyItem:%d\"",
696                                   num_property_items);
697                g_string_sprintfa (xml, " verb=\"EStorageSetView:FolderPropertyItem:%d\"",
698                                   num_property_items);
699
700                encoded_tooltip = bonobo_ui_util_encode_str (item->tooltip);
701                g_string_sprintfa (xml, " tip=\"%s\"", encoded_tooltip);
702
703                encoded_label = bonobo_ui_util_encode_str (item->label);
704                g_string_sprintfa (xml, " label=\"%s\"/>", encoded_label);
705
706                g_free (encoded_tooltip);
707                g_free (encoded_label);
708        }
709
710        g_string_append (xml, "</placeholder>");
711
712        data = g_new (FolderPropertyItemsData, 1);
713        data->storage_set_view = storage_set_view;
714        data->corba_storage    = E_CORBA_STORAGE (storage);
715        data->num_items        = num_property_items;
716
717        gtk_object_ref (GTK_OBJECT (data->storage_set_view));
718        gtk_object_ref (GTK_OBJECT (data->corba_storage));
719
720        for (i = 1; i <= num_property_items; i ++) {
721                char *verb;
722
723                verb = g_strdup_printf ("EStorageSetView:FolderPropertyItem:%d", i);
724                bonobo_ui_component_add_verb (priv->ui_component, verb,
725                                              folder_property_item_verb_callback,
726                                              data);
727        }
728
729        bonobo_ui_component_set (priv->ui_component, "/popups/FolderPopup", xml->str, NULL);
730
731        g_string_free (xml, TRUE);
732        e_corba_storage_free_property_items_list (items);
733
734        return data;
735}
736
737static void
738remove_property_items (EStorageSetView *storage_set_view,
739                       FolderPropertyItemsData *data)
740{
741        EStorageSetViewPrivate *priv;
742
743        priv = storage_set_view->priv;
744
745        if (data->num_items > 0) {
746                int i;
747
748                bonobo_ui_component_rm (priv->ui_component,
749                                        "/popups/FolderPopup/StorageFolderPropertiesPlaceholder/EStorageSetViewFolderPropertiesSeparator",
750                                        NULL);
751
752                for (i = 1; i <= data->num_items; i ++) {
753                        char *path;
754                        char *verb;
755
756                        path = g_strdup_printf ("/popups/FolderPopup/StorageFolderPropertiesPlaceholder/EStorageSetView:FolderPropertyItem:%d", i);
757                        bonobo_ui_component_rm (priv->ui_component, path, NULL);
758                        g_free (path);
759
760                        verb = g_strdup_printf ("EStorageSetView:FolderPropertyItem:%d", i);
761                        bonobo_ui_component_remove_verb (priv->ui_component, verb);
762                        g_free (verb);
763                }
764        }
765
766        gtk_object_unref (GTK_OBJECT (data->storage_set_view));
767        gtk_object_unref (GTK_OBJECT (data->corba_storage));
768
769        g_free (data);
770}
771
772static void
773popup_folder_menu (EStorageSetView *storage_set_view,
774                   GdkEventButton *event)
775{
776        EvolutionShellComponentClient *handler;
777        EStorageSetViewPrivate *priv;
778        EFolderTypeRegistry *folder_type_registry;
779        EFolder *folder;
780        GtkWidget *menu;
781        FolderPropertyItemsData *folder_property_items_data;
782
783        priv = storage_set_view->priv;
784
785        folder = e_storage_set_get_folder (priv->storage_set, priv->right_click_row_path);
786        gtk_object_ref (GTK_OBJECT (folder));
787
788        folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set);
789        g_assert (folder_type_registry != NULL);
790
791        handler = e_folder_type_registry_get_handler_for_type (folder_type_registry,
792                                                               e_folder_get_type_string (folder));
793        menu = gtk_menu_new ();
794        bonobo_window_add_popup (bonobo_ui_container_get_win (priv->ui_container),
795                                 GTK_MENU (menu), "/popups/FolderPopup");
796
797        bonobo_ui_component_set (priv->ui_component,
798                                 "/popups/FolderPopup/ComponentPlaceholder",
799                                 "<placeholder name=\"Items\"/>", NULL);
800
801        if (handler != NULL)
802                evolution_shell_component_client_populate_folder_context_menu (handler,
803                                                                               priv->ui_container,
804                                                                               e_folder_get_physical_uri (folder),
805                                                                               e_folder_get_type_string (folder));
806
807        folder_property_items_data = setup_folder_properties_items_if_corba_storage_clicked (storage_set_view);
808
809        gtk_widget_show (GTK_WIDGET (menu));
810
811        gnome_popup_menu_do_popup_modal (GTK_WIDGET (menu), NULL, NULL, event, NULL);
812
813        if (folder_property_items_data != NULL)
814                remove_property_items (storage_set_view, folder_property_items_data);
815
816        if (handler != NULL)
817                evolution_shell_component_client_unpopulate_folder_context_menu (handler,
818                                                                                 priv->ui_container,
819                                                                                 e_folder_get_physical_uri (folder),
820                                                                                 e_folder_get_type_string (folder));
821
822        gtk_object_unref (GTK_OBJECT (folder));
823        gtk_widget_destroy (GTK_WIDGET (menu));
824
825        e_tree_right_click_up (E_TREE (storage_set_view));
826}
827
828
829/* GtkObject methods.  */
830
831static void
832path_free_func (gpointer key, gpointer value, gpointer user_data)
833{
834        g_free (key);
835}
836
837static void
838pixbuf_free_func (gpointer key, gpointer value, gpointer user_data)
839{
840        g_free (key);
841        gdk_pixbuf_unref ((GdkPixbuf*)value);
842}
843
844static void
845impl_destroy (GtkObject *object)
846{
847        EStorageSetView *storage_set_view;
848        EStorageSetViewPrivate *priv;
849
850        storage_set_view = E_STORAGE_SET_VIEW (object);
851        priv = storage_set_view->priv;
852
853        /* need to destroy our tree */
854        e_tree_memory_node_remove (E_TREE_MEMORY(priv->etree_model), priv->root_node);
855        gtk_object_unref (GTK_OBJECT (priv->etree_model));
856
857        /* the data in the hash table was all freed by freeing the tree */
858        g_hash_table_foreach (priv->path_to_etree_node, path_free_func, NULL);
859        g_hash_table_destroy (priv->path_to_etree_node);
860
861        /* now free up all the type_names and pixbufs stored in the
862           hash table and destroy the hash table itself */
863        g_hash_table_foreach (priv->type_name_to_pixbuf, pixbuf_free_func, NULL);
864        g_hash_table_destroy (priv->type_name_to_pixbuf);
865
866        if (priv->checkboxes) {
867                g_hash_table_foreach (priv->checkboxes, (GHFunc) g_free, NULL);
868                g_hash_table_destroy (priv->checkboxes);
869                priv->checkboxes = NULL;
870        }
871
872        gtk_object_unref (GTK_OBJECT (priv->storage_set));
873
874        if (priv->drag_corba_source_interface != CORBA_OBJECT_NIL) {
875                CORBA_Environment ev;
876
877                CORBA_exception_init (&ev);
878
879                g_assert (priv->drag_corba_source_context != NULL);
880
881                GNOME_Evolution_ShellComponentDnd_SourceFolder_endDrag (priv->drag_corba_source_interface,
882                                                                        priv->drag_corba_source_context,
883                                                                        &ev);
884
885                Bonobo_Unknown_unref (priv->drag_corba_source_interface, &ev);
886                CORBA_Object_release (priv->drag_corba_source_interface, &ev);
887
888                CORBA_exception_free (&ev);
889        }
890
891        if (priv->drag_corba_source_context != NULL)
892                CORBA_free (priv->drag_corba_source_context);
893
894        if (priv->drag_corba_data != NULL)
895                CORBA_free (priv->drag_corba_data);
896
897        if (priv->ui_component != NULL)
898                bonobo_object_unref (BONOBO_OBJECT (priv->ui_component));
899
900        /* (No unreffing for priv->ui_container since we use a weakref.)  */
901
902        g_free (priv->selected_row_path);
903        g_free (priv->right_click_row_path);
904
905        g_free (priv);
906
907        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
908}
909
910
911/* ETree methods.  */
912
913/* -- Source-side DnD.  */
914
915static gint
916impl_tree_start_drag (ETree *tree,
917                      int row,
918                      ETreePath path,
919                      int col,
920                      GdkEvent *event)
921{
922        GdkDragContext *context;
923        GtkTargetList *target_list;
924        GdkDragAction actions;
925        EStorageSetView *storage_set_view;
926
927        storage_set_view = E_STORAGE_SET_VIEW (tree);
928
929        if (! storage_set_view->priv->allow_dnd)
930                return FALSE;
931
932        target_list = create_target_list_for_node (storage_set_view, path);
933        if (target_list == NULL)
934                return FALSE;
935
936        actions = GDK_ACTION_MOVE | GDK_ACTION_COPY;
937
938        context = e_tree_drag_begin (tree, row, col,
939                                     target_list,
940                                     actions,
941                                     1, event);
942
943        gtk_drag_set_icon_default (context);
944
945        gtk_target_list_unref (target_list);
946
947        return TRUE;
948}
949
950static void
951impl_tree_drag_begin (ETree *etree,
952                      int row,
953                      ETreePath path,
954                      int col,
955                      GdkDragContext *context)
956{
957        EStorageSetView *storage_set_view;
958        EStorageSetViewPrivate *priv;
959        EFolder *folder;
960        EvolutionShellComponentClient *component_client;
961        GNOME_Evolution_ShellComponent corba_component;
962        GNOME_Evolution_ShellComponentDnd_ActionSet possible_actions;
963        GNOME_Evolution_ShellComponentDnd_Action suggested_action;
964        CORBA_Environment ev;
965
966        storage_set_view = E_STORAGE_SET_VIEW (etree);
967        priv = storage_set_view->priv;
968
969        g_free (priv->selected_row_path);
970        priv->selected_row_path = g_strdup (e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path));
971
972        g_assert (priv->drag_corba_source_interface == CORBA_OBJECT_NIL);
973
974        folder = get_folder_at_node (storage_set_view, path);
975        component_client = get_component_at_node (storage_set_view, path);
976
977        if (component_client == NULL)
978                return;
979
980        /* Query the `ShellComponentDnd::SourceFolder' interface on the
981           component.  */
982        /* FIXME we could use the new
983           `evolution_shell_component_client_get_dnd_source_interface()'
984           call. */
985
986        CORBA_exception_init (&ev);
987
988        corba_component = bonobo_object_corba_objref (BONOBO_OBJECT (component_client));
989        priv->drag_corba_source_interface = Bonobo_Unknown_queryInterface (corba_component,
990                                                                           "IDL:GNOME/Evolution/ShellComponentDnd/SourceFolder:1.0",
991                                                                           &ev);
992        if (ev._major != CORBA_NO_EXCEPTION
993            || priv->drag_corba_source_interface == CORBA_OBJECT_NIL) {
994                priv->drag_corba_source_interface = CORBA_OBJECT_NIL;
995
996                CORBA_exception_free (&ev);
997                return;
998        }
999
1000        GNOME_Evolution_ShellComponentDnd_SourceFolder_beginDrag (priv->drag_corba_source_interface,
1001                                                                  e_folder_get_physical_uri (folder),
1002                                                                  e_folder_get_type_string (folder),
1003                                                                  &possible_actions,
1004                                                                  &suggested_action,
1005                                                                  &ev);
1006
1007        if (ev._major != CORBA_NO_EXCEPTION) {
1008                Bonobo_Unknown_unref (priv->drag_corba_source_interface, &ev);
1009                CORBA_Object_release (priv->drag_corba_source_interface, &ev);
1010
1011                priv->drag_corba_source_interface = CORBA_OBJECT_NIL;
1012
1013                CORBA_exception_free (&ev);
1014                return;
1015        }
1016
1017        CORBA_exception_free (&ev);
1018
1019        if (priv->drag_corba_source_context != NULL)
1020                CORBA_free (priv->drag_corba_source_context);
1021
1022        priv->drag_corba_source_context = GNOME_Evolution_ShellComponentDnd_SourceFolder_Context__alloc ();
1023        priv->drag_corba_source_context->physicalUri     = CORBA_string_dup (e_folder_get_physical_uri (folder));
1024        priv->drag_corba_source_context->folderType      = CORBA_string_dup (e_folder_get_type_string (folder));
1025        priv->drag_corba_source_context->possibleActions = possible_actions;
1026        priv->drag_corba_source_context->suggestedAction = suggested_action;
1027}
1028
1029static void
1030impl_tree_drag_end (ETree *tree,
1031                    int row,
1032                    ETreePath path,
1033                    int col,
1034                    GdkDragContext *context)
1035{
1036        EStorageSetView *storage_set_view;
1037        EStorageSetViewPrivate *priv;
1038        CORBA_Environment ev;
1039
1040        storage_set_view = E_STORAGE_SET_VIEW (tree);
1041        priv = storage_set_view->priv;
1042
1043        if (priv->drag_corba_source_interface == CORBA_OBJECT_NIL)
1044                return;
1045
1046        CORBA_exception_init (&ev);
1047
1048        GNOME_Evolution_ShellComponentDnd_SourceFolder_endDrag (priv->drag_corba_source_interface,
1049                                                                priv->drag_corba_source_context,
1050                                                                &ev);
1051
1052        CORBA_free (priv->drag_corba_source_context);
1053        priv->drag_corba_source_context = NULL;
1054
1055        Bonobo_Unknown_unref (priv->drag_corba_source_interface, &ev);
1056        CORBA_Object_release (priv->drag_corba_source_interface, &ev);
1057
1058        CORBA_exception_free (&ev);
1059}
1060
1061static void
1062impl_tree_drag_data_get (ETree *etree,
1063                         int drag_row,
1064                         ETreePath drag_path,
1065                         int drag_col,
1066                         GdkDragContext *context,
1067                         GtkSelectionData *selection_data,
1068                         unsigned int info,
1069                         guint32 time)
1070{
1071        EStorageSetView *storage_set_view;
1072        EStorageSetViewPrivate *priv;
1073        CORBA_Environment ev;
1074        char *target_type;
1075
1076        storage_set_view = E_STORAGE_SET_VIEW (etree);
1077        priv = storage_set_view->priv;
1078
1079        switch (info) {
1080        case E_SHORTCUT_TARGET_TYPE_IDX:
1081                set_e_shortcut_selection (storage_set_view, selection_data);
1082                return;
1083        case E_FOLDER_DND_PATH_TARGET_TYPE_IDX:
1084                set_evolution_path_selection (storage_set_view, selection_data);
1085                return;
1086        }
1087
1088        g_assert (info > 0);
1089
1090        if (priv->drag_corba_source_interface == CORBA_OBJECT_NIL)
1091                return;
1092
1093        target_type = gdk_atom_name ((GdkAtom) context->targets->data);
1094
1095        CORBA_exception_init (&ev);
1096
1097        GNOME_Evolution_ShellComponentDnd_SourceFolder_getData (priv->drag_corba_source_interface,
1098                                                                priv->drag_corba_source_context,
1099                                                                convert_gdk_drag_action_set_to_corba (context->action),
1100                                                                target_type,
1101                                                                & priv->drag_corba_data,
1102                                                                &ev);
1103
1104        if (ev._major != CORBA_NO_EXCEPTION)
1105                gtk_selection_data_set (selection_data, selection_data->target, 8, "", -1);
1106        else
1107                gtk_selection_data_set (selection_data,
1108                                        priv->drag_corba_data->target,
1109                                        priv->drag_corba_data->format,
1110                                        priv->drag_corba_data->bytes._buffer,
1111                                        priv->drag_corba_data->bytes._length);
1112
1113        g_free (target_type);
1114
1115        CORBA_exception_free (&ev);
1116}
1117
1118static void
1119impl_tree_drag_data_delete (ETree *tree,
1120                            int row,
1121                            ETreePath path,
1122                            int col,
1123                            GdkDragContext *context)
1124{
1125        EStorageSetView *storage_set_view;
1126        EStorageSetViewPrivate *priv;
1127        CORBA_Environment ev;
1128
1129        storage_set_view = E_STORAGE_SET_VIEW (tree);
1130        priv = storage_set_view->priv;
1131
1132        if (priv->drag_corba_source_interface == CORBA_OBJECT_NIL)
1133                return;
1134
1135        CORBA_exception_init (&ev);
1136
1137        GNOME_Evolution_ShellComponentDnd_SourceFolder_deleteData (priv->drag_corba_source_interface,
1138                                                                   priv->drag_corba_source_context,
1139                                                                   &ev);
1140
1141        CORBA_exception_free (&ev);
1142}
1143
1144/* -- Destination-side DnD.  */
1145
1146static gboolean
1147impl_tree_drag_motion (ETree *tree,
1148                       int row,
1149                       ETreePath path,
1150                       int col,
1151                       GdkDragContext *context,
1152                       int x,
1153                       int y,
1154                       unsigned int time)
1155{
1156        EStorageSetView *storage_set_view;
1157        EStorageSetViewPrivate *priv;
1158        const char *folder_path;
1159
1160        storage_set_view = E_STORAGE_SET_VIEW (tree);
1161        priv = storage_set_view->priv;
1162
1163        if (! priv->allow_dnd)
1164                return FALSE;
1165
1166        folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model),
1167                                            e_tree_node_at_row (E_TREE (storage_set_view), row));
1168        if (folder_path == NULL)
1169                return FALSE;
1170
1171        e_tree_drag_highlight (E_TREE (storage_set_view), row, -1);
1172
1173        return e_folder_dnd_bridge_motion (GTK_WIDGET (storage_set_view), context, time,
1174                                           priv->storage_set, folder_path);
1175}
1176
1177static void
1178impl_tree_drag_leave (ETree *etree,
1179                      int row,
1180                      ETreePath path,
1181                      int col,
1182                      GdkDragContext *context,
1183                      unsigned int time)
1184{
1185        e_tree_drag_unhighlight (etree);
1186}
1187
1188static gboolean
1189impl_tree_drag_drop (ETree *etree,
1190                     int row,
1191                     ETreePath path,
1192                     int col,
1193                     GdkDragContext *context,
1194                     int x,
1195                     int y,
1196                     unsigned int time)
1197{
1198        EStorageSetView *storage_set_view;
1199        EStorageSetViewPrivate *priv;
1200        const char *folder_path;
1201
1202        storage_set_view = E_STORAGE_SET_VIEW (etree);
1203        priv = storage_set_view->priv;
1204
1205        e_tree_drag_unhighlight (etree);
1206
1207        folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model),
1208                                                   e_tree_node_at_row (E_TREE (storage_set_view), row));
1209        if (folder_path == NULL)
1210                return FALSE;
1211
1212        return e_folder_dnd_bridge_drop (GTK_WIDGET (etree), context, time,
1213                                         priv->storage_set, folder_path);
1214}
1215
1216static void
1217impl_tree_drag_data_received (ETree *etree,
1218                              int row,
1219                              ETreePath path,
1220                              int col,
1221                              GdkDragContext *context,
1222                              int x,
1223                              int y,
1224                              GtkSelectionData *selection_data,
1225                              unsigned int info,
1226                              unsigned int time)
1227{
1228        EStorageSetView *storage_set_view;
1229        EStorageSetViewPrivate *priv;
1230        const char *folder_path;
1231
1232        storage_set_view = E_STORAGE_SET_VIEW (etree);
1233        priv = storage_set_view->priv;
1234
1235        folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model),
1236                                                   e_tree_node_at_row (E_TREE (storage_set_view), row));
1237        if (path == NULL) {
1238                gtk_drag_finish (context, FALSE, FALSE, time);
1239                return;
1240        }
1241
1242        e_folder_dnd_bridge_data_received  (GTK_WIDGET (etree),
1243                                            context,
1244                                            selection_data,
1245                                            time,
1246                                            priv->storage_set,
1247                                            folder_path);
1248}
1249
1250static gboolean
1251impl_right_click (ETree *etree,
1252                  int row,
1253                  ETreePath path,
1254                  int col,
1255                  GdkEvent *event)
1256{
1257        EStorageSetView *storage_set_view;
1258        EStorageSetViewPrivate *priv;
1259
1260        storage_set_view = E_STORAGE_SET_VIEW (etree);
1261        priv = storage_set_view->priv;
1262
1263        /* This should never happen, but you never know with ETree.  */
1264        if (priv->right_click_row_path != NULL)
1265                g_free (priv->right_click_row_path);
1266        priv->right_click_row_path = g_strdup (e_tree_memory_node_get_data (E_TREE_MEMORY(priv->etree_model), path));
1267
1268        if (priv->ui_container) {
1269                gtk_signal_emit (GTK_OBJECT (storage_set_view),
1270                                 signals[FOLDER_CONTEXT_MENU_POPPING_UP],
1271                                 priv->right_click_row_path);
1272
1273                popup_folder_menu (storage_set_view, (GdkEventButton *) event);
1274
1275                gtk_signal_emit (GTK_OBJECT (storage_set_view),
1276                                 signals[FOLDER_CONTEXT_MENU_POPPED_DOWN]);
1277        }
1278
1279        g_free (priv->right_click_row_path);
1280        priv->right_click_row_path = NULL;
1281
1282        return TRUE;
1283}
1284
1285static void
1286impl_cursor_activated (ETree *tree,
1287                       int row,
1288                       ETreePath path)
1289{
1290        EStorageSetView *storage_set_view;
1291        EStorageSetViewPrivate *priv;
1292
1293        storage_set_view = E_STORAGE_SET_VIEW (tree);
1294
1295        priv = storage_set_view->priv;
1296
1297        g_free (priv->selected_row_path);
1298        if (path) {
1299                priv->selected_row_path = g_strdup (e_tree_memory_node_get_data (E_TREE_MEMORY (priv->etree_model), path));
1300
1301                gtk_signal_emit (GTK_OBJECT (storage_set_view), signals[FOLDER_SELECTED],
1302                                 priv->selected_row_path);
1303        }
1304        else
1305                priv->selected_row_path = NULL;
1306}
1307
1308
1309/* ETreeModel Methods */
1310
1311static gboolean
1312path_is_storage (ETreeModel *etree,
1313                 ETreePath tree_path)
1314{
1315        return e_tree_model_node_depth (etree, tree_path) == 1;
1316}
1317
1318static GdkPixbuf*
1319etree_icon_at (ETreeModel *etree,
1320               ETreePath tree_path,
1321               void *model_data)
1322{
1323        EStorageSetView *storage_set_view;
1324        EStorageSet *storage_set;
1325        EFolder *folder;
1326        char *path;
1327
1328        storage_set_view = E_STORAGE_SET_VIEW (model_data);
1329        storage_set = storage_set_view->priv->storage_set;
1330
1331        path = (char*) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), tree_path);
1332
1333        folder = e_storage_set_get_folder (storage_set, path);
1334        if (folder == NULL)
1335                return NULL;
1336               
1337        /* No icon for a storage with children (or with no real root folder) */
1338        if (path_is_storage (etree, tree_path)) {
1339                EStorage *storage;
1340                GList *subfolder_paths;
1341
1342                if (! strcmp (e_folder_get_type_string (folder), "noselect"))
1343                        return NULL;
1344
1345                storage = e_storage_set_get_storage (storage_set, path + 1);
1346                subfolder_paths = e_storage_get_subfolder_paths (storage, "/");
1347                if (subfolder_paths != NULL) {
1348                        e_free_string_list (subfolder_paths);
1349                        return NULL;
1350                }
1351        }
1352
1353        return get_pixbuf_for_folder (storage_set_view, folder);
1354}
1355
1356/* This function returns the number of columns in our ETreeModel. */
1357static int
1358etree_column_count (ETreeModel *etc,
1359                    void *data)
1360{
1361        return 3;
1362}
1363
1364static gboolean
1365etree_has_save_id (ETreeModel *etm,
1366                   void *data)
1367{
1368        return TRUE;
1369}
1370
1371static gchar *
1372etree_get_save_id (ETreeModel *etm,
1373                   ETreePath node,
1374                   void *model_data)
1375{
1376        return g_strdup(e_tree_memory_node_get_data (E_TREE_MEMORY(etm), node));
1377}
1378
1379static gboolean
1380etree_has_get_node_by_id (ETreeModel *etm,
1381                          void *data)
1382{
1383        return TRUE;
1384}
1385
1386static ETreePath
1387etree_get_node_by_id (ETreeModel *etm,
1388                      const char *save_id,
1389                      void *model_data)
1390{
1391        EStorageSetView *storage_set_view;
1392        storage_set_view = E_STORAGE_SET_VIEW (model_data);
1393
1394        return g_hash_table_lookup (storage_set_view->priv->path_to_etree_node, save_id);
1395}
1396
1397static gboolean
1398has_checkbox (EStorageSetView *storage_set_view, ETreePath tree_path)
1399{
1400        EStorageSetViewPrivate *priv;
1401        const char *folder_path;
1402
1403        priv = storage_set_view->priv;
1404
1405        folder_path = e_tree_memory_node_get_data (E_TREE_MEMORY(storage_set_view->priv->etree_model),
1406                                                   tree_path);
1407        g_assert (folder_path != NULL);
1408
1409        if (strchr (folder_path + 1, '/') == NULL) {
1410                /* If it's a toplevel, never allow checking it.  */
1411                return FALSE;
1412        }
1413
1414        if (priv->has_checkbox_func)
1415                return (* priv->has_checkbox_func) (priv->storage_set,
1416                                                    folder_path,
1417                                                    priv->has_checkbox_func_data);
1418
1419        return TRUE;
1420}
1421
1422static void *
1423etree_value_at (ETreeModel *etree,
1424                ETreePath tree_path,
1425                int col,
1426                void *model_data)
1427{
1428        EStorageSetView *storage_set_view;
1429        EStorageSetViewPrivate *priv;
1430        EStorageSet *storage_set;
1431        EFolder *folder;
1432        char *path;
1433        const char *folder_name;
1434        int unread_count;
1435
1436        storage_set_view = E_STORAGE_SET_VIEW (model_data);
1437        priv = storage_set_view->priv;
1438        storage_set = priv->storage_set;
1439
1440        /* Storages are always highlighted. */
1441        if (col == 1 && path_is_storage (etree, tree_path))
1442                return (void *) TRUE;
1443
1444        path = (char *) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), tree_path);
1445
1446        folder = e_storage_set_get_folder (storage_set, path);
1447
1448        switch (col) {
1449        case 0: /* Title */
1450                if (folder == NULL)
1451                        return (void *) "?";
1452                folder_name = e_folder_get_name (folder);
1453                unread_count = e_folder_get_unread_count (folder);
1454
1455                if (unread_count > 0) {
1456                        char *name_with_unread;
1457
1458                        name_with_unread = g_strdup_printf ("%s (%d)", folder_name,
1459                                                            unread_count);
1460                        gtk_object_set_data_full (GTK_OBJECT (folder),
1461                                                  "name_with_unread",
1462                                                  name_with_unread, g_free);
1463
1464                        return (void *) name_with_unread;
1465                } else
1466                        return (void *) folder_name;
1467        case 1: /* bold */
1468                if (folder == NULL)
1469                        return GINT_TO_POINTER (FALSE);
1470                return GINT_TO_POINTER (e_folder_get_highlighted (folder));
1471        case 2: /* checkbox */
1472                if (!has_checkbox (storage_set_view, tree_path))
1473                        return GINT_TO_POINTER (2);
1474                if (priv->checkboxes == NULL)
1475                        return GINT_TO_POINTER (0);
1476                return GINT_TO_POINTER(g_hash_table_lookup (priv->checkboxes,
1477                                                            path) ? 1 : 0);
1478        default:
1479                return NULL;
1480        }
1481
1482}
1483
1484static void
1485etree_fill_in_children (ETreeModel *etree,
1486                        ETreePath tree_path,
1487                        void *model_data)
1488{
1489        EStorageSetView *storage_set_view;
1490        EStorageSet *storage_set;
1491        ETreePath *parent;
1492        char *path;
1493
1494        storage_set_view = E_STORAGE_SET_VIEW (model_data);
1495        storage_set = storage_set_view->priv->storage_set;
1496
1497        parent = e_tree_model_node_get_parent (etree, tree_path);
1498        path = (char *) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), parent);
1499        if (tree_path == e_tree_model_node_get_first_child (etree, parent)) {
1500                gtk_signal_emit (GTK_OBJECT (storage_set_view),
1501                                 signals[FOLDER_OPENED], path);
1502        }
1503}
1504
1505static void
1506etree_set_value_at (ETreeModel *etree,
1507                    ETreePath tree_path,
1508                    int col,
1509                    const void *val,
1510                    void *model_data)
1511{
1512        gboolean value;
1513        char *path;
1514        EStorageSetView *storage_set_view;
1515        EStorageSetViewPrivate *priv;
1516        char *old_path;
1517
1518        storage_set_view = E_STORAGE_SET_VIEW (model_data);
1519        priv = storage_set_view->priv;
1520
1521        switch (col) {
1522        case 2: /* checkbox */
1523                if (!has_checkbox (storage_set_view, tree_path))
1524                        return;
1525
1526                e_tree_model_pre_change (etree);
1527
1528                value = GPOINTER_TO_INT (val);
1529                path = (char *) e_tree_memory_node_get_data (E_TREE_MEMORY(etree), tree_path);
1530                if (!priv->checkboxes) {
1531                        priv->checkboxes = g_hash_table_new (g_str_hash, g_str_equal);
1532                }
1533
1534                old_path = g_hash_table_lookup (priv->checkboxes, path);
1535
1536                if (old_path) {
1537                        g_hash_table_remove (priv->checkboxes, path);
1538                        g_free (old_path);
1539                } else {
1540                        path = g_strdup (path);
1541                        g_hash_table_insert (priv->checkboxes, path, path);
1542                }
1543
1544                e_tree_model_node_col_changed (etree, tree_path, col);
1545                gtk_signal_emit (GTK_OBJECT (storage_set_view),
1546                                 signals[CHECKBOXES_CHANGED]);
1547                break;
1548        }
1549}
1550
1551static gboolean
1552etree_is_editable (ETreeModel *etree,
1553                   ETreePath path,
1554                   int col,
1555                   void *model_data)
1556{
1557        if (col == 2)
1558                return TRUE;
1559        else
1560                return FALSE;
1561}
1562
1563
1564/* This function duplicates the value passed to it. */
1565static void *
1566etree_duplicate_value (ETreeModel *etc,
1567                       int col,
1568                       const void *value,
1569                       void *data)
1570{
1571        if (col == 0)
1572                return (void *)g_strdup (value);
1573        else
1574                return (void *)value;
1575}
1576
1577/* This function frees the value passed to it. */
1578static void
1579etree_free_value (ETreeModel *etc,
1580                  int col,
1581                  void *value,
1582                  void *data)
1583{
1584        if (col == 0)
1585                g_free (value);
1586}
1587
1588/* This function creates an empty value. */
1589static void *
1590etree_initialize_value (ETreeModel *etc,
1591                        int col,
1592                        void *data)
1593{
1594        if (col == 0)
1595                return g_strdup ("");
1596        else
1597                return NULL;
1598}
1599
1600/* This function reports if a value is empty. */
1601static gboolean
1602etree_value_is_empty (ETreeModel *etc,
1603                      int col,
1604                      const void *value,
1605                      void *data)
1606{
1607        if (col == 0)
1608                return !(value && *(char *)value);
1609        else
1610                return !value;
1611}
1612
1613/* This function reports if a value is empty. */
1614static char *
1615etree_value_to_string (ETreeModel *etc,
1616                       int col,
1617                       const void *value,
1618                       void *data)
1619{
1620        if (col == 0)
1621                return g_strdup(value);
1622        else
1623                return g_strdup(value ? "Yes" : "No");
1624}
1625
1626static void
1627etree_node_destroy_func (void *data,
1628                         void *user_data)
1629{
1630        EStorageSetView *storage_set_view;
1631        char *path;
1632
1633        path = (char *) data;
1634        storage_set_view = E_STORAGE_SET_VIEW (user_data);
1635
1636        if (strcmp (path, ROOT_NODE_NAME))
1637                remove_node_from_hash (storage_set_view, path);
1638        g_free (path);
1639}
1640
1641
1642/* StorageSet signal handling.  */
1643
1644static void
1645new_storage_cb (EStorageSet *storage_set,
1646                EStorage *storage,
1647                void *data)
1648{
1649        EStorageSetView *storage_set_view;
1650        EStorageSetViewPrivate *priv;
1651        ETreePath node;
1652        char *path;
1653
1654        storage_set_view = E_STORAGE_SET_VIEW (data);
1655        priv = storage_set_view->priv;
1656
1657        path = g_strconcat (E_PATH_SEPARATOR_S, e_storage_get_name (storage), NULL);
1658
1659        node = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), priv->root_node, -1, path);
1660        e_tree_memory_sort_node (E_TREE_MEMORY(priv->etree_model), priv->root_node,
1661                                 storage_sort_callback, storage_set_view);
1662
1663        if (! add_node_to_hash (storage_set_view, path, node)) {
1664                e_tree_memory_node_remove (E_TREE_MEMORY(priv->etree_model), node);
1665                return;
1666        }
1667}
1668
1669static void
1670removed_storage_cb (EStorageSet *storage_set,
1671                    EStorage *storage,
1672                    void *data)
1673{
1674        EStorageSetView *storage_set_view;
1675        EStorageSetViewPrivate *priv;
1676        ETreeModel *etree;
1677        ETreePath node;
1678        char *path;
1679
1680        storage_set_view = E_STORAGE_SET_VIEW (data);
1681        priv = storage_set_view->priv;
1682        etree = priv->etree_model;
1683
1684        path = g_strconcat (E_PATH_SEPARATOR_S, e_storage_get_name (storage), NULL);
1685        node = lookup_node_in_hash (storage_set_view, path);
1686        g_free (path);
1687
1688        e_tree_memory_node_remove (E_TREE_MEMORY(etree), node);
1689}
1690
1691static void
1692new_folder_cb (EStorageSet *storage_set,
1693               const char *path,
1694               void *data)
1695{
1696        EStorageSetView *storage_set_view;
1697        EStorageSetViewPrivate *priv;
1698        ETreeModel *etree;
1699        ETreePath parent_node;
1700        ETreePath new_node;
1701        const char *last_separator;
1702        char *parent_path;
1703        char *copy_of_path;
1704
1705        g_return_if_fail (g_path_is_absolute (path));
1706
1707        storage_set_view = E_STORAGE_SET_VIEW (data);
1708        priv = storage_set_view->priv;
1709        etree = priv->etree_model;
1710
1711        last_separator = strrchr (path, E_PATH_SEPARATOR);
1712
1713        parent_path = g_strndup (path, last_separator - path);
1714        parent_node = g_hash_table_lookup (priv->path_to_etree_node, parent_path);
1715        if (parent_node == NULL) {
1716                g_warning ("EStorageSetView: EStorageSet reported new subfolder for non-existing folder -- %s",
1717                           parent_path);
1718                g_free (parent_path);
1719                return;
1720        }
1721
1722        g_free (parent_path);
1723
1724        copy_of_path = g_strdup (path);
1725        new_node = e_tree_memory_node_insert (E_TREE_MEMORY(etree), parent_node, -1, copy_of_path);
1726        e_tree_memory_sort_node (E_TREE_MEMORY(etree), parent_node, folder_sort_callback, storage_set_view);
1727
1728        if (! add_node_to_hash (storage_set_view, path, new_node)) {
1729                e_tree_memory_node_remove (E_TREE_MEMORY(etree), new_node);
1730                return;
1731        }
1732
1733        setup_folder_changed_callbacks (storage_set_view,
1734                                        e_storage_set_get_folder (storage_set, path),
1735                                        path);
1736}
1737
1738static void
1739updated_folder_cb (EStorageSet *storage_set,
1740                   const char *path,
1741                   void *data)
1742{
1743        EStorageSetView *storage_set_view;
1744        EStorageSetViewPrivate *priv;
1745        ETreeModel *etree;
1746        ETreePath node;
1747
1748        storage_set_view = E_STORAGE_SET_VIEW (data);
1749        priv = storage_set_view->priv;
1750        etree = priv->etree_model;
1751
1752        node = lookup_node_in_hash (storage_set_view, path);
1753        e_tree_model_pre_change (etree);
1754        e_tree_model_node_data_changed (etree, node);
1755}
1756
1757static void
1758removed_folder_cb (EStorageSet *storage_set,
1759                   const char *path,
1760                   void *data)
1761{
1762        EStorageSetView *storage_set_view;
1763        EStorageSetViewPrivate *priv;
1764        ETreeModel *etree;
1765        ETreePath node;
1766
1767        storage_set_view = E_STORAGE_SET_VIEW (data);
1768        priv = storage_set_view->priv;
1769        etree = priv->etree_model;
1770
1771        node = lookup_node_in_hash (storage_set_view, path);
1772        e_tree_memory_node_remove (E_TREE_MEMORY(etree), node);
1773}
1774
1775static void
1776close_folder_cb (EStorageSet *storage_set,
1777                 const char *path,
1778                 void *data)
1779{
1780        EStorageSetView *storage_set_view;
1781        EStorageSetViewPrivate *priv;
1782        ETreeModel *etree;
1783        ETreePath node;
1784
1785        storage_set_view = E_STORAGE_SET_VIEW (data);
1786        priv = storage_set_view->priv;
1787        etree = priv->etree_model;
1788
1789        node = lookup_node_in_hash (storage_set_view, path);
1790        e_tree_model_node_request_collapse (priv->etree_model, node);
1791}
1792
1793
1794static void
1795class_init (EStorageSetViewClass *klass)
1796{
1797        GtkObjectClass *object_class;
1798        ETreeClass *etree_class;
1799
1800        parent_class = gtk_type_class (PARENT_TYPE);
1801
1802        object_class = GTK_OBJECT_CLASS (klass);
1803        object_class->destroy = impl_destroy;
1804
1805        etree_class = E_TREE_CLASS (klass);
1806        etree_class->right_click             = impl_right_click;
1807        etree_class->cursor_activated        = impl_cursor_activated;
1808        etree_class->start_drag              = impl_tree_start_drag;
1809        etree_class->tree_drag_begin         = impl_tree_drag_begin;
1810        etree_class->tree_drag_end           = impl_tree_drag_end;
1811        etree_class->tree_drag_data_get      = impl_tree_drag_data_get;
1812        etree_class->tree_drag_data_delete   = impl_tree_drag_data_delete;
1813        etree_class->tree_drag_motion        = impl_tree_drag_motion;
1814        etree_class->tree_drag_drop          = impl_tree_drag_drop;
1815        etree_class->tree_drag_leave         = impl_tree_drag_leave;
1816        etree_class->tree_drag_data_received = impl_tree_drag_data_received;
1817
1818        signals[FOLDER_SELECTED]
1819                = gtk_signal_new ("folder_selected",
1820                                  GTK_RUN_FIRST,
1821                                  object_class->type,
1822                                  GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_selected),
1823                                  gtk_marshal_NONE__STRING,
1824                                  GTK_TYPE_NONE, 1,
1825                                  GTK_TYPE_STRING);
1826
1827        signals[FOLDER_OPENED]
1828                = gtk_signal_new ("folder_opened",
1829                                  GTK_RUN_FIRST,
1830                                  object_class->type,
1831                                  GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_opened),
1832                                  gtk_marshal_NONE__STRING,
1833                                  GTK_TYPE_NONE, 1,
1834                                  GTK_TYPE_STRING);
1835
1836        signals[DND_ACTION]
1837                = gtk_signal_new ("dnd_action",
1838                                  GTK_RUN_FIRST,
1839                                  object_class->type,
1840                                  GTK_SIGNAL_OFFSET (EStorageSetViewClass, dnd_action),
1841                                  marshal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING,
1842                                  GTK_TYPE_NONE, 4,
1843                                  GTK_TYPE_GDK_DRAG_CONTEXT,
1844                                  GTK_TYPE_STRING,
1845                                  GTK_TYPE_STRING,
1846                                  GTK_TYPE_STRING);
1847
1848        signals[FOLDER_CONTEXT_MENU_POPPING_UP]
1849                = gtk_signal_new ("folder_context_menu_popping_up",
1850                                  GTK_RUN_FIRST,
1851                                  object_class->type,
1852                                  GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_context_menu_popping_up),
1853                                  gtk_marshal_NONE__STRING,
1854                                  GTK_TYPE_NONE, 1,
1855                                  GTK_TYPE_STRING);
1856
1857        signals[FOLDER_CONTEXT_MENU_POPPED_DOWN]
1858                = gtk_signal_new ("folder_context_menu_popped_down",
1859                                  GTK_RUN_FIRST,
1860                                  object_class->type,
1861                                  GTK_SIGNAL_OFFSET (EStorageSetViewClass, folder_context_menu_popped_down),
1862                                  gtk_marshal_NONE__NONE,
1863                                  GTK_TYPE_NONE, 0);
1864
1865        signals[CHECKBOXES_CHANGED]
1866                = gtk_signal_new ("checkboxes_changed",
1867                                  GTK_RUN_FIRST,
1868                                  object_class->type,
1869                                  GTK_SIGNAL_OFFSET (EStorageSetViewClass, checkboxes_changed),
1870                                  gtk_marshal_NONE__NONE,
1871                                  GTK_TYPE_NONE, 0);
1872
1873        gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
1874
1875        checks [0] = gdk_pixbuf_new_from_xpm_data (check_empty_xpm);
1876        checks [1] = gdk_pixbuf_new_from_xpm_data (check_filled_xpm);
1877        checks [2] = gdk_pixbuf_new_from_xpm_data (check_missing_xpm);
1878}
1879
1880static void
1881init (EStorageSetView *storage_set_view)
1882{
1883        EStorageSetViewPrivate *priv;
1884
1885        priv = g_new (EStorageSetViewPrivate, 1);
1886
1887        priv->storage_set                 = NULL;
1888        priv->path_to_etree_node          = g_hash_table_new (g_str_hash, g_str_equal);
1889        priv->type_name_to_pixbuf         = g_hash_table_new (g_str_hash, g_str_equal);
1890
1891        priv->ui_component                = NULL;
1892        priv->ui_container                = NULL;
1893
1894        priv->selected_row_path           = NULL;
1895        priv->right_click_row_path        = NULL;
1896
1897        priv->show_folders                = TRUE;
1898        priv->show_checkboxes             = FALSE;
1899        priv->allow_dnd                   = TRUE;
1900        priv->search_enabled              = FALSE;
1901
1902        priv->drag_corba_source_interface = CORBA_OBJECT_NIL;
1903
1904        priv->drag_corba_source_context   = NULL;
1905        priv->drag_corba_data             = NULL;
1906
1907        priv->checkboxes                  = NULL;
1908
1909        priv->has_checkbox_func           = NULL;
1910        priv->has_checkbox_func_data      = NULL;
1911
1912        storage_set_view->priv = priv;
1913}
1914
1915
1916/* Handling of the "changed" signal in EFolders displayed in the EStorageSetView.  */
1917
1918struct _FolderChangedCallbackData {
1919        EStorageSetView *storage_set_view;
1920        char *path;
1921};
1922typedef struct _FolderChangedCallbackData FolderChangedCallbackData;
1923
1924static void
1925folder_changed_callback_data_destroy_notify (void *data)
1926{
1927        FolderChangedCallbackData *callback_data;
1928
1929        callback_data = (FolderChangedCallbackData *) data;
1930
1931        g_free (callback_data->path);
1932        g_free (callback_data);
1933}
1934
1935static void
1936folder_changed_cb (EFolder *folder,
1937                   void *data)
1938{
1939        EStorageSetView *storage_set_view;
1940        EStorageSetViewPrivate *priv;
1941        FolderChangedCallbackData *callback_data;
1942        ETreePath node;
1943
1944        callback_data = (FolderChangedCallbackData *) data;
1945
1946        storage_set_view = callback_data->storage_set_view;
1947        priv = callback_data->storage_set_view->priv;
1948
1949        node = g_hash_table_lookup (priv->path_to_etree_node, callback_data->path);
1950        if (node == NULL) {
1951                g_warning ("EStorageSetView -- EFolder::changed emitted for a folder whose path I don't know.");
1952                return;
1953        }
1954
1955        e_tree_model_pre_change (priv->etree_model);
1956        e_tree_model_node_data_changed (priv->etree_model, node);
1957}
1958
1959static void
1960folder_name_changed_cb (EFolder *folder,
1961                        void *data)
1962{
1963        EStorageSetView *storage_set_view;
1964        EStorageSetViewPrivate *priv;
1965        FolderChangedCallbackData *callback_data;
1966        ETreePath parent_node;
1967        const char *last_separator;
1968        char *parent_path;
1969
1970        callback_data = (FolderChangedCallbackData *) data;
1971
1972        storage_set_view = callback_data->storage_set_view;
1973        priv = storage_set_view->priv;
1974
1975        last_separator = strrchr (callback_data->path, E_PATH_SEPARATOR);
1976
1977        parent_path = g_strndup (callback_data->path, last_separator - callback_data->path);
1978        parent_node = g_hash_table_lookup (priv->path_to_etree_node, parent_path);
1979        g_free (parent_path);
1980
1981        if (parent_node == NULL) {
1982                g_warning ("EStorageSetView -- EFolder::name_changed emitted for a folder whose path I don't know.");
1983                return;
1984        }
1985
1986        e_tree_memory_sort_node (E_TREE_MEMORY (priv->etree_model), parent_node,
1987                                 folder_sort_callback, storage_set_view);
1988}
1989
1990static void
1991setup_folder_changed_callbacks (EStorageSetView *storage_set_view,
1992                                EFolder *folder,
1993                                const char *path)
1994{
1995        FolderChangedCallbackData *folder_changed_callback_data;
1996
1997        folder_changed_callback_data = g_new (FolderChangedCallbackData, 1);
1998        folder_changed_callback_data->storage_set_view = storage_set_view;
1999        folder_changed_callback_data->path = g_strdup (path);
2000
2001        gtk_signal_connect_while_alive (GTK_OBJECT (folder), "name_changed",
2002                                        GTK_SIGNAL_FUNC (folder_name_changed_cb),
2003                                        folder_changed_callback_data,
2004                                        GTK_OBJECT (storage_set_view));
2005
2006        e_gtk_signal_connect_full_while_alive (GTK_OBJECT (folder), "changed",
2007                                               GTK_SIGNAL_FUNC (folder_changed_cb),
2008                                               NULL,
2009                                               folder_changed_callback_data,
2010                                               folder_changed_callback_data_destroy_notify,
2011                                               FALSE, FALSE,
2012                                               GTK_OBJECT (storage_set_view));
2013}
2014
2015
2016static void
2017insert_folders (EStorageSetView *storage_set_view,
2018                ETreePath parent,
2019                EStorage *storage,
2020                const char *path)
2021{
2022        EStorageSetViewPrivate *priv;
2023        ETreeModel *etree;
2024        ETreePath node;
2025        GList *folder_path_list;
2026        GList *p;
2027        const char *storage_name;
2028
2029        priv = storage_set_view->priv;
2030        etree = priv->etree_model;
2031
2032        storage_name = e_storage_get_name (storage);
2033
2034        folder_path_list = e_storage_get_subfolder_paths (storage, path);
2035        if (folder_path_list == NULL)
2036                return;
2037
2038        for (p = folder_path_list; p != NULL; p = p->next) {
2039                EFolder *folder;
2040                const char *folder_name;
2041                const char *folder_path;
2042                char *full_path;
2043
2044                folder_path = (const char *) p->data;
2045                folder = e_storage_get_folder (storage, folder_path);
2046                folder_name = e_folder_get_name (folder);
2047
2048                full_path = g_strconcat ("/", storage_name, folder_path, NULL);
2049
2050                setup_folder_changed_callbacks (storage_set_view, folder, full_path);
2051
2052                node = e_tree_memory_node_insert (E_TREE_MEMORY(etree), parent, -1, (void *) full_path);
2053                e_tree_memory_sort_node(E_TREE_MEMORY(etree), parent, folder_sort_callback, storage_set_view);
2054                add_node_to_hash (storage_set_view, full_path, node);
2055
2056                insert_folders (storage_set_view, node, storage, folder_path);
2057        }
2058
2059        e_free_string_list (folder_path_list);
2060}
2061
2062static void
2063insert_storages (EStorageSetView *storage_set_view)
2064{
2065        EStorageSetViewPrivate *priv;
2066        EStorageSet *storage_set;
2067        GList *storage_list;
2068        GList *p;
2069
2070        priv = storage_set_view->priv;
2071
2072        storage_set = priv->storage_set;
2073
2074        storage_list = e_storage_set_get_storage_list (storage_set);
2075
2076        for (p = storage_list; p != NULL; p = p->next) {
2077                EStorage *storage = E_STORAGE (p->data);
2078                const char *name;
2079                char *path;
2080                ETreePath parent;
2081
2082                name = e_storage_get_name (storage);
2083                path = g_strconcat ("/", name, NULL);
2084
2085                parent = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), priv->root_node, -1, path);
2086                e_tree_memory_sort_node (E_TREE_MEMORY(priv->etree_model),
2087                                         priv->root_node,
2088                                         storage_sort_callback, storage_set_view);
2089
2090                g_hash_table_insert (priv->path_to_etree_node, path, parent);
2091
2092                if (priv->show_folders)
2093                        insert_folders (storage_set_view, parent, storage, "/");
2094        }
2095
2096        e_free_object_list (storage_list);
2097}
2098
2099void
2100e_storage_set_view_construct (EStorageSetView   *storage_set_view,
2101                              EStorageSet       *storage_set,
2102                              BonoboUIContainer *ui_container)
2103{
2104        EStorageSetViewPrivate *priv;
2105        ETableExtras *extras;
2106        ECell *cell;
2107
2108        g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
2109        g_return_if_fail (E_IS_STORAGE_SET (storage_set));
2110
2111        priv = storage_set_view->priv;
2112
2113        priv->ui_container = ui_container;
2114        if (ui_container != NULL) {
2115                gtk_object_weakref (GTK_OBJECT (ui_container), ui_container_destroy_notify, priv);
2116
2117                priv->ui_component = bonobo_ui_component_new_default ();
2118                bonobo_ui_component_set_container (priv->ui_component,
2119                                                   bonobo_object_corba_objref (BONOBO_OBJECT (ui_container)));
2120        }
2121
2122        priv->etree_model = e_tree_memory_callbacks_new (etree_icon_at,
2123
2124                                                         etree_column_count,
2125
2126                                                         etree_has_save_id,
2127                                                         etree_get_save_id,
2128                                                         etree_has_get_node_by_id,
2129                                                         etree_get_node_by_id,
2130
2131                                                         etree_value_at,
2132                                                         etree_set_value_at,
2133                                                         etree_is_editable,
2134
2135                                                         etree_duplicate_value,
2136                                                         etree_free_value,
2137                                                         etree_initialize_value,
2138                                                         etree_value_is_empty,
2139                                                         etree_value_to_string,
2140
2141                                                         storage_set_view);
2142
2143        e_tree_memory_set_node_destroy_func (E_TREE_MEMORY (priv->etree_model),
2144                                             etree_node_destroy_func, storage_set_view);
2145        e_tree_memory_set_expanded_default (E_TREE_MEMORY (priv->etree_model), FALSE);
2146
2147        priv->root_node = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), NULL, -1,
2148                                                     g_strdup (ROOT_NODE_NAME));
2149        add_node_to_hash (storage_set_view, ROOT_NODE_NAME, priv->root_node);
2150
2151        extras = e_table_extras_new ();
2152        cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
2153        gtk_object_set (GTK_OBJECT (cell), "bold_column", 1, NULL);
2154        e_table_extras_add_cell (extras, "render_tree",
2155                                 e_cell_tree_new (NULL, NULL, TRUE, cell));
2156
2157        e_table_extras_add_cell (extras, "optional_checkbox",
2158                                 e_cell_toggle_new (2, 3, checks));
2159
2160        e_tree_construct_from_spec_file (E_TREE (storage_set_view), priv->etree_model, extras,
2161                                         EVOLUTION_ETSPECDIR "/e-storage-set-view.etspec", NULL);
2162
2163        e_tree_root_node_set_visible (E_TREE(storage_set_view), FALSE);
2164
2165        gtk_object_unref (GTK_OBJECT (extras));
2166
2167        gtk_object_ref (GTK_OBJECT (storage_set));
2168        priv->storage_set = storage_set;
2169
2170        e_tree_drag_dest_set (E_TREE (storage_set_view), 0, NULL, 0, GDK_ACTION_MOVE | GDK_ACTION_COPY);
2171
2172        gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "new_storage",
2173                                        GTK_SIGNAL_FUNC (new_storage_cb), storage_set_view,
2174                                        GTK_OBJECT (storage_set_view));
2175        gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "removed_storage",
2176                                        GTK_SIGNAL_FUNC (removed_storage_cb), storage_set_view,
2177                                        GTK_OBJECT (storage_set_view));
2178        gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "new_folder",
2179                                        GTK_SIGNAL_FUNC (new_folder_cb), storage_set_view,
2180                                        GTK_OBJECT (storage_set_view));
2181        gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "updated_folder",
2182                                        GTK_SIGNAL_FUNC (updated_folder_cb), storage_set_view,
2183                                        GTK_OBJECT (storage_set_view));
2184        gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "removed_folder",
2185                                        GTK_SIGNAL_FUNC (removed_folder_cb), storage_set_view,
2186                                        GTK_OBJECT (storage_set_view));
2187        gtk_signal_connect_while_alive (GTK_OBJECT (storage_set), "close_folder",
2188                                        GTK_SIGNAL_FUNC (close_folder_cb), storage_set_view,
2189                                        GTK_OBJECT (storage_set_view));
2190
2191        gtk_signal_connect_while_alive (GTK_OBJECT (priv->etree_model), "fill_in_children",
2192                                        GTK_SIGNAL_FUNC (etree_fill_in_children), storage_set_view,
2193                                        GTK_OBJECT (storage_set_view));
2194
2195        insert_storages (storage_set_view);
2196}
2197
2198/* DON'T USE THIS. Use e_storage_set_new_view() instead. */
2199GtkWidget *
2200e_storage_set_view_new (EStorageSet *storage_set,
2201                        BonoboUIContainer *ui_container)
2202{
2203        GtkWidget *new;
2204
2205        g_return_val_if_fail (E_IS_STORAGE_SET (storage_set), NULL);
2206
2207        new = gtk_type_new (e_storage_set_view_get_type ());
2208
2209        e_storage_set_view_construct (E_STORAGE_SET_VIEW (new), storage_set, ui_container);
2210
2211        return new;
2212}
2213
2214
2215EStorageSet *
2216e_storage_set_view_get_storage_set (EStorageSetView *storage_set_view)
2217{
2218        EStorageSetViewPrivate *priv;
2219
2220        g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), NULL);
2221
2222        priv = storage_set_view->priv;
2223        return priv->storage_set;
2224}
2225
2226void
2227e_storage_set_view_set_current_folder (EStorageSetView *storage_set_view,
2228                                       const char *path)
2229{
2230        EStorageSetViewPrivate *priv;
2231        ETreePath node;
2232
2233        g_return_if_fail (storage_set_view != NULL);
2234        g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
2235        g_return_if_fail (path != NULL && g_path_is_absolute (path));
2236
2237        priv = storage_set_view->priv;
2238
2239        node = g_hash_table_lookup (priv->path_to_etree_node, path);
2240        if (node == NULL)
2241                return;
2242
2243        e_tree_show_node (E_TREE (storage_set_view), node);
2244        e_tree_set_cursor (E_TREE (storage_set_view), node);
2245
2246        g_free (priv->selected_row_path);
2247        priv->selected_row_path = g_strdup (path);
2248
2249        gtk_signal_emit (GTK_OBJECT (storage_set_view), signals[FOLDER_SELECTED], path);
2250}
2251
2252const char *
2253e_storage_set_view_get_current_folder (EStorageSetView *storage_set_view)
2254{
2255        EStorageSetViewPrivate *priv;
2256        ETreePath etree_node;
2257        const char *path;
2258
2259        g_return_val_if_fail (storage_set_view != NULL, NULL);
2260        g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), NULL);
2261
2262        priv = storage_set_view->priv;
2263
2264        if (!priv->show_folders)
2265                return NULL; /* Mmh! */
2266
2267        etree_node = e_tree_get_cursor (E_TREE (storage_set_view));
2268
2269        if (etree_node == NULL)
2270                return NULL; /* Mmh? */
2271
2272        path = (char*)e_tree_memory_node_get_data(E_TREE_MEMORY(priv->etree_model), etree_node);
2273
2274        return path;
2275}
2276
2277void
2278e_storage_set_view_set_show_folders (EStorageSetView *storage_set_view,
2279                                     gboolean show)
2280{
2281        EStorageSetViewPrivate *priv;
2282
2283        g_return_if_fail (storage_set_view != NULL);
2284        g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
2285
2286        priv = storage_set_view->priv;
2287
2288        if (show == priv->show_folders)
2289                return;
2290
2291        /* tear down existing tree and hash table mappings */
2292        e_tree_memory_node_remove (E_TREE_MEMORY(priv->etree_model), priv->root_node);
2293
2294        /* now re-add the root node */
2295        priv->root_node = e_tree_memory_node_insert (E_TREE_MEMORY(priv->etree_model), NULL, -1,
2296                                                     g_strdup (ROOT_NODE_NAME));
2297        add_node_to_hash (storage_set_view, ROOT_NODE_NAME, priv->root_node);
2298
2299        /* then reinsert the storages after setting the "show_folders"
2300           flag.  insert_storages will call insert_folders if
2301           show_folders is TRUE */
2302
2303        priv->show_folders = show;
2304        insert_storages (storage_set_view);
2305}
2306
2307gboolean
2308e_storage_set_view_get_show_folders (EStorageSetView *storage_set_view)
2309{
2310        return storage_set_view->priv->show_folders;
2311}
2312
2313
2314
2315void
2316e_storage_set_view_set_show_checkboxes (EStorageSetView *storage_set_view,
2317                                        gboolean show,
2318                                        EStorageSetViewHasCheckBoxFunc has_checkbox_func,
2319                                        void *func_data)
2320{
2321        EStorageSetViewPrivate *priv;
2322        ETableState *state;
2323
2324        g_return_if_fail (storage_set_view != NULL);
2325        g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
2326
2327        priv = storage_set_view->priv;
2328
2329        show = !! show;
2330
2331        if (show == priv->show_checkboxes)
2332                return;
2333
2334        priv->show_checkboxes = show;
2335
2336        state = e_tree_get_state_object (E_TREE (storage_set_view));
2337        g_free (state->columns);
2338        state->col_count = show ? 2 : 1;
2339        state->columns = g_new(int, state->col_count);
2340        state->columns [state->col_count - 1] = 0;
2341        if (show)
2342                state->columns [0] = 1;
2343        e_tree_set_state_object (E_TREE (storage_set_view), state);
2344
2345        gtk_object_unref (GTK_OBJECT (state));
2346
2347        priv->has_checkbox_func = has_checkbox_func;
2348        priv->has_checkbox_func_data = func_data;
2349}
2350
2351gboolean
2352e_storage_set_view_get_show_checkboxes (EStorageSetView *storage_set_view)
2353{
2354        g_return_val_if_fail (storage_set_view != NULL, FALSE);
2355        g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), FALSE);
2356
2357        return storage_set_view->priv->show_checkboxes;
2358}
2359
2360void
2361e_storage_set_view_enable_search (EStorageSetView *storage_set_view,
2362                                  gboolean enable)
2363{
2364        g_return_if_fail (storage_set_view != NULL);
2365        g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
2366
2367        enable = !! enable;
2368
2369        if (enable == storage_set_view->priv->search_enabled)
2370                return;
2371       
2372        storage_set_view->priv->search_enabled = enable;
2373        e_tree_set_search_column (E_TREE (storage_set_view), enable ? 0 : -1);
2374}
2375
2376void
2377e_storage_set_view_set_checkboxes_list (EStorageSetView *storage_set_view,
2378                                        GList           *checkboxes)
2379{
2380        gboolean changed = FALSE;
2381        EStorageSetViewPrivate *priv = storage_set_view->priv;
2382
2383        e_tree_model_pre_change (priv->etree_model);
2384        if (priv->checkboxes) {
2385                g_hash_table_foreach (priv->checkboxes, (GHFunc) g_free, NULL);
2386                g_hash_table_destroy (priv->checkboxes);
2387                changed = TRUE;
2388        }
2389
2390        if (checkboxes) {
2391                priv->checkboxes = g_hash_table_new (g_str_hash, g_str_equal);
2392                for (; checkboxes; checkboxes = g_list_next (checkboxes)) {
2393                        char *path = checkboxes->data;
2394
2395                        if (g_hash_table_lookup (priv->checkboxes, path))
2396                                continue;
2397                        path = g_strdup (path);
2398                        g_hash_table_insert (priv->checkboxes, path, path);
2399                }
2400                changed = TRUE;
2401        }
2402
2403        if (changed)
2404                e_tree_model_node_changed (priv->etree_model,
2405                                           e_tree_model_get_root (priv->etree_model));
2406        else
2407                e_tree_model_no_change (priv->etree_model);
2408}
2409
2410static void
2411essv_add_to_list (gpointer      key,
2412                  gpointer      value,
2413                  gpointer      user_data)
2414{
2415        GList **list = user_data;
2416
2417        *list = g_list_prepend (*list, g_strdup (key));
2418}
2419
2420GList *
2421e_storage_set_view_get_checkboxes_list (EStorageSetView *storage_set_view)
2422{
2423        GList *list = NULL;
2424
2425        if (storage_set_view->priv->checkboxes) {
2426                g_hash_table_foreach (storage_set_view->priv->checkboxes, essv_add_to_list, &list);
2427
2428                list = g_list_reverse (list);
2429        }
2430        return list;
2431}
2432
2433
2434void
2435e_storage_set_view_set_allow_dnd (EStorageSetView *storage_set_view,
2436                                  gboolean allow_dnd)
2437{
2438        g_return_if_fail (storage_set_view != NULL);
2439        g_return_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view));
2440
2441        storage_set_view->priv->allow_dnd = !! allow_dnd;
2442}
2443
2444gboolean
2445e_storage_set_view_get_allow_dnd (EStorageSetView *storage_set_view)
2446{
2447        g_return_val_if_fail (storage_set_view != NULL, FALSE);
2448        g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), FALSE);
2449
2450        return storage_set_view->priv->allow_dnd;
2451}
2452
2453const char *
2454e_storage_set_view_get_right_click_path (EStorageSetView *storage_set_view)
2455{
2456        g_return_val_if_fail (storage_set_view != NULL, NULL);
2457        g_return_val_if_fail (E_IS_STORAGE_SET_VIEW (storage_set_view), NULL);
2458
2459        return storage_set_view->priv->right_click_row_path;
2460}
2461
2462
2463E_MAKE_TYPE (e_storage_set_view, "EStorageSetView", EStorageSetView, class_init, init, PARENT_TYPE)
Note: See TracBrowser for help on using the repository browser.