source: trunk/third/evolution/mail/subscribe-dialog.c @ 16787

Revision 16787, 41.9 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16786, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/* subscribe-dialog.c: Subscribe dialog */
3/*
4 *  Authors: Chris Toshok <toshok@ximian.com>
5 *           Peter Williams <peterw@ximian.com>
6 *
7 *  Copyright 2000 Ximian, Inc. (www.ximian.com)
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU General Public
11 * License as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <gal/util/e-util.h>
30#include <gal/widgets/e-unicode.h>
31
32#include <gal/e-table/e-cell-toggle.h>
33#include <gal/e-table/e-cell-text.h>
34#include <gal/e-table/e-cell-tree.h>
35
36#include <gal/e-table/e-tree-scrolled.h>
37#include <gal/e-table/e-tree-memory-callbacks.h>
38#include <gal/e-table/e-tree.h>
39
40#include <pthread.h>
41
42#include "evolution-shell-component-utils.h"
43#include "mail.h"
44#include "mail-tools.h"
45#include "mail-ops.h"
46#include "mail-mt.h"
47#include "mail-folder-cache.h"
48#include "camel/camel-exception.h"
49#include "camel/camel-store.h"
50#include "camel/camel-session.h"
51#include "subscribe-dialog.h"
52
53#include "art/empty.xpm"
54#include "art/mark.xpm"
55
56#define d(x)
57
58/* Things to test.
59 * - Feature
60 *   + How to check that it works.
61 *
62 * - Proper stores displayed
63 *   + Skip stores that don't support subscriptions
64 *   + Skip disabled stores
65 * - Changing subscription status
66 *   + Select single folder, double-click row -> toggled
67 *   + Select multiple folders, press subscribe -> all selected folders end up subscribed
68 * - (un)Subscribing from/to already (un)subscribed folder
69 *   + Check that no IMAP command is sent
70 * - Switching views between stores
71 *   + Proper tree shown
72 * - No crashes when buttons are pressed with "No store" screen
73 *   + obvious
74 * - Restoring filter settings when view switched
75 *   + Enter search, change view, change back -> filter checked and search entry set
76 *   + Clear search, change view, change back -> "all" checked
77 * - Cancelling in middle of get_store
78 *   + Enter invalid hostname, open dialog, click Close
79 * - Cancelling in middle if listing
80 *   + Open large directory, click Close
81 * - Cancelling in middle of subscription op
82 *   + How to test?
83 * - Test with both IMAP and NNTP
84 *   + obvious
85 * - Verify that refresh view works
86 *   + obvious
87 * - No unnecessary tree rebuilds
88 *   + Show All folders, change filter with empty search -> no tree rebuild
89 *   + Converse
90 * - No out of date tree
91 *   + Show All Folders, change to filter with a search -> tree rebuild
92 * - Tree construction logic (mostly IMAP-specific terminology)
93 *   + Tree is created progressively
94 *   + No wasted LIST responses
95 *   + No extraneous LIST commands
96 *   + Specifying "folder names begin with" works
97 *   + Always show folders below IMAP namespace (no escaping the namespace)
98 *   + Don't allow subscription to NoSelect folders
99 *   + IMAP accounts always show INBOX
100 * - Shell interactions
101 *   + Folders are properly created / delete from folder tree when subscribed / unsubscribed
102 *   + Folders with spaces in names / 8bit chars
103 *   + Toplevel as well as subfolders
104 *   + Mail Folder Cache doesn't complain
105 * - No ETable wackiness
106 *   + Verify columns cannot be DnD'd
107 *   + Alphabetical order always
108 * - UI cleanliness
109 *   + Keybindings work
110 *   + Some widget has focus by default
111 *   + Escape / enter work
112 *   + Close button works
113 */
114
115/* FIXME: we should disable/enable the subscribe/unsubscribe buttons as
116 * appropriate when only a single message is selected. We need a
117 * mechanism to learn when the selected folder's subscription status
118 * changes, so when the user double-clicks it (eg) the buttons can
119 * (de)sensitize appropriately. See Ximian bug #7673.
120 */
121
122/*#define NEED_TOGGLE_SELECTION*/
123
124typedef struct _FolderETree              FolderETree;
125typedef struct _FolderETreeClass         FolderETreeClass;
126
127struct _FolderETree {
128        ETreeMemory parent;
129        ETreePath root;
130
131        GHashTable *scan_ops;
132        GHashTable *subscribe_ops;
133
134        CamelStore *store;
135        EvolutionStorage *e_storage;
136        char *service_name;
137        char *search;
138};
139
140struct _FolderETreeClass {
141        ETreeMemoryClass parent;
142};
143
144static GtkObjectClass *folder_etree_parent_class = NULL;
145
146typedef struct _FolderETreeExtras      FolderETreeExtras;
147typedef struct _FolderETreeExtrasClass FolderETreeExtrasClass;
148
149enum {
150        FOLDER_COL_SUBSCRIBED,
151        FOLDER_COL_NAME,
152        FOLDER_COL_LAST
153};
154
155struct _FolderETreeExtras {
156        ETableExtras parent;
157        GdkPixbuf *toggles[2];
158};
159
160struct _FolderETreeExtrasClass {
161        ETableExtrasClass parent;
162};
163
164static GtkObjectClass *ftree_extras_parent_class = NULL;
165
166/* util */
167
168static void
169recursive_add_folder (EvolutionStorage *storage, const char *path, const char *name, const char *url)
170{
171        char *parent, *pname, *p;
172
173        p = strrchr (path, '/');
174        if (p && p != path) {
175                parent = g_strndup (path, p - path);
176                if (!evolution_storage_folder_exists (storage, parent)) {
177                        p = strrchr (parent, '/');
178                        if (p)
179                                pname = g_strdup (p + 1);
180                        else
181                                pname = g_strdup ("");
182                        recursive_add_folder (storage, parent, pname, "");
183                        g_free (pname);
184                }
185                g_free (parent);
186        }
187
188        evolution_storage_new_folder (storage, path, name, "mail", url, name, FALSE);
189}
190
191/* ** Get one level of folderinfo ****************************************** */
192
193typedef void (*SubscribeShortFolderinfoFunc) (CamelStore *store, char *prefix, CamelFolderInfo *info, gpointer data);
194
195int subscribe_get_short_folderinfo (FolderETree *ftree, const char *prefix,
196                                    SubscribeShortFolderinfoFunc func, gpointer user_data);
197
198struct _get_short_folderinfo_msg {
199        struct _mail_msg msg;
200
201        char *prefix;
202
203        FolderETree *ftree;
204        CamelFolderInfo *info;
205
206        SubscribeShortFolderinfoFunc func;
207        gpointer user_data;
208};
209
210static char *
211get_short_folderinfo_desc (struct _mail_msg *mm, int done)
212{
213        struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
214        char *ret, *name;
215
216        name = camel_service_get_name (CAMEL_SERVICE (m->ftree->store), TRUE);
217
218        if (m->prefix)
219                ret = g_strdup_printf (_("Scanning folders under %s on \"%s\""), m->prefix, name);
220        else
221                ret = g_strdup_printf (_("Scanning root-level folders on \"%s\""), name);
222
223        g_free (name);
224        return ret;
225}
226
227static void
228get_short_folderinfo_get (struct _mail_msg *mm)
229{
230        struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
231
232        m->info = camel_store_get_folder_info (m->ftree->store, m->prefix, CAMEL_STORE_FOLDER_INFO_FAST, &mm->ex);
233}
234
235static void
236get_short_folderinfo_got (struct _mail_msg *mm)
237{
238        struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
239
240        if (camel_exception_is_set (&mm->ex))
241                g_warning ("Error getting folder info from store at %s: %s",
242                           camel_service_get_url (CAMEL_SERVICE (m->ftree->store)),
243                           camel_exception_get_description (&mm->ex));
244
245        /* 'done' is probably guaranteed to fail, but... */
246
247        if (m->func)
248                m->func (m->ftree->store, m->prefix, m->info, m->user_data);
249}
250
251static void
252get_short_folderinfo_free (struct _mail_msg *mm)
253{
254        struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
255
256        camel_store_free_folder_info (m->ftree->store, m->info);
257        gtk_object_unref (GTK_OBJECT (m->ftree));
258
259        g_free (m->prefix); /* may be NULL but that's ok */
260}
261
262static struct _mail_msg_op get_short_folderinfo_op = {
263        get_short_folderinfo_desc,
264        get_short_folderinfo_get,
265        get_short_folderinfo_got,
266        get_short_folderinfo_free,
267};
268
269int
270subscribe_get_short_folderinfo (FolderETree *ftree,
271                                const char *prefix,
272                                SubscribeShortFolderinfoFunc func,
273                                gpointer user_data)
274{
275        struct _get_short_folderinfo_msg *m;
276        int id;
277
278        m = mail_msg_new (&get_short_folderinfo_op, NULL, sizeof(*m));
279
280        m->ftree = ftree;
281        gtk_object_ref (GTK_OBJECT (ftree));
282       
283        if (prefix)
284                m->prefix = g_strdup (prefix);
285        else
286                m->prefix = NULL;
287
288        m->func = func;
289        m->user_data = user_data;
290
291        id = m->msg.seq;
292        e_thread_put (mail_thread_queued, (EMsg *)m);
293        return id;
294}
295
296/* ** Subscribe folder operation **************************************** */
297
298typedef void (*SubscribeFolderCallback) (const char *, const char *, gboolean, gboolean, gpointer);
299
300struct _subscribe_msg {
301        struct _mail_msg         msg;
302
303        CamelStore              *store;
304        gboolean                 subscribe;
305        char                    *full_name;
306        char                    *name;
307
308        SubscribeFolderCallback  cb;
309        gpointer                 cb_data;
310};
311
312static char *
313subscribe_folder_desc (struct _mail_msg *mm, int done)
314{
315        struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
316
317        if (m->subscribe)
318                return g_strdup_printf (_("Subscribing to folder \"%s\""), m->name);
319        else
320                return g_strdup_printf (_("Unsubscribing to folder \"%s\""), m->name);
321}
322
323static void
324subscribe_folder_subscribe (struct _mail_msg *mm)
325{
326        struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
327       
328        if (m->subscribe)
329                camel_store_subscribe_folder (m->store, m->full_name, &mm->ex);
330        else
331                camel_store_unsubscribe_folder (m->store, m->full_name, &mm->ex);
332}
333
334static void
335subscribe_folder_subscribed (struct _mail_msg *mm)
336{
337        struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
338       
339        if (m->cb)
340                (m->cb) (m->full_name, m->name, m->subscribe,
341                         !camel_exception_is_set (&mm->ex), m->cb_data);
342}
343
344static void
345subscribe_folder_free (struct _mail_msg *mm)
346{
347        struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
348
349        g_free (m->name);
350        g_free (m->full_name);
351       
352        camel_object_unref (CAMEL_OBJECT (m->store));
353}
354
355static struct _mail_msg_op subscribe_folder_op = {
356        subscribe_folder_desc,
357        subscribe_folder_subscribe,
358        subscribe_folder_subscribed,
359        subscribe_folder_free,
360};
361
362static int
363subscribe_do_subscribe_folder (CamelStore *store, const char *full_name, const char *name,
364                               gboolean subscribe, SubscribeFolderCallback cb, gpointer cb_data)
365{
366        struct _subscribe_msg *m;
367        int id;
368
369        g_return_val_if_fail (CAMEL_IS_STORE (store), 0);
370        g_return_val_if_fail (full_name, 0);
371
372        m            = mail_msg_new (&subscribe_folder_op, NULL, sizeof(*m));
373        m->store     = store;
374        m->subscribe = subscribe;
375        m->name      = g_strdup (name);
376        m->full_name = g_strdup (full_name);
377        m->cb        = cb;
378        m->cb_data   = cb_data;
379
380        camel_object_ref (CAMEL_OBJECT (store));
381
382        id = m->msg.seq;
383        e_thread_put (mail_thread_queued, (EMsg *)m);
384        return id;
385}
386
387/* ** FolderETree Extras *************************************************** */
388
389static void
390fete_destroy (GtkObject *object)
391{
392        FolderETreeExtras *extras = (FolderETreeExtras *) object;
393
394        gdk_pixbuf_unref (extras->toggles[0]);
395        gdk_pixbuf_unref (extras->toggles[1]);
396
397        ftree_extras_parent_class->destroy (object);
398}
399
400static void
401fete_class_init (GtkObjectClass *object_class)
402{
403        object_class->destroy = fete_destroy;
404
405        ftree_extras_parent_class = gtk_type_class (E_TABLE_EXTRAS_TYPE);
406}
407
408static void
409fete_init (GtkObject *object)
410{
411        FolderETreeExtras *extras = (FolderETreeExtras *) object;
412        ECell             *cell;
413        ECell             *text_cell;
414
415        /* text column */
416
417        cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
418        text_cell = cell;
419        gtk_object_set (GTK_OBJECT (cell),
420                        "bold_column", FOLDER_COL_SUBSCRIBED,
421                        NULL);
422        e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_text", cell);
423
424        /* toggle column */
425
426        extras->toggles[0] = gdk_pixbuf_new_from_xpm_data ((const char **)empty_xpm);
427        extras->toggles[1] = gdk_pixbuf_new_from_xpm_data ((const char **)mark_xpm);
428        cell = e_cell_toggle_new (0, 2, extras->toggles);
429        e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_toggle", cell);
430
431        /* tree cell */
432
433        cell = e_cell_tree_new (NULL, NULL, TRUE, text_cell);
434        e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_tree", cell);
435
436        /* misc */
437
438        e_table_extras_add_pixbuf (E_TABLE_EXTRAS (extras), "subscribed-image", extras->toggles[1]);
439}
440
441/* naughty! */
442static
443E_MAKE_TYPE (fete, "FolderETreeExtras", FolderETreeExtras, fete_class_init, fete_init, E_TABLE_EXTRAS_TYPE);
444
445/* ** Global Extras ******************************************************** */
446
447static FolderETreeExtras *global_extras = NULL;
448
449static void
450global_extras_destroyed (GtkObject *obj, gpointer user_data)
451{
452        global_extras = NULL;
453}
454
455static ETableExtras *
456subscribe_get_global_extras (void)
457{
458        if (global_extras == NULL) {
459                global_extras = gtk_type_new (fete_get_type());
460                gtk_object_ref (GTK_OBJECT (global_extras));
461                gtk_object_sink (GTK_OBJECT (global_extras));
462                gtk_signal_connect (GTK_OBJECT (global_extras), "destroy",
463                                    global_extras_destroyed, NULL);
464        }
465
466        gtk_object_ref (GTK_OBJECT (global_extras));
467        return E_TABLE_EXTRAS (global_extras);
468}
469
470/* ** Folder Tree Node ***************************************************** */
471
472typedef struct _ftree_node ftree_node;
473
474struct _ftree_node {
475        guint8    flags;
476        char *cache;
477        int uri_offset;
478        int full_name_offset;
479
480        /* format: {name}{\0}{uri}{\0}{full_name}{\0}
481         * (No braces). */
482        char data[1];
483};
484
485#define FTREE_NODE_GOT_CHILDREN (1 << 0)
486#define FTREE_NODE_SUBSCRIBABLE (1 << 1)
487#define FTREE_NODE_SUBSCRIBED   (1 << 2)
488#define FTREE_NODE_ROOT         (1 << 3)
489
490static ftree_node *
491ftree_node_new_root (const char *prefix)
492{
493        ftree_node *node;
494        size_t      size;
495
496        if (prefix == NULL)
497                prefix = "";
498
499        size = sizeof (ftree_node) + strlen (prefix) + 1;
500
501        node = g_malloc (size);
502        node->flags = FTREE_NODE_ROOT;
503        node->uri_offset = 0;
504        node->full_name_offset = 1;
505        node->data[0] = '\0';
506        strcpy (node->data + 1, prefix);
507       
508        return node;
509}
510
511static ftree_node *
512ftree_node_new (CamelStore *store, CamelFolderInfo *fi)
513{
514        ftree_node *node;
515        int         uri_offset, full_name_offset;
516        size_t      size;
517        CamelURL   *url;
518
519        uri_offset       = strlen (fi->name) + 1;
520        full_name_offset = uri_offset + strlen (fi->url) + 1;
521        size             = full_name_offset + strlen (fi->full_name);
522 
523        /* - 1 for sizeof(node.data) but +1 for terminating \0 */
524        node = g_malloc (sizeof (*node) + size);
525
526        node->cache = NULL;
527       
528        /* Noselect? */
529
530        url = camel_url_new (fi->url, NULL);
531        if (camel_url_get_param (url, "noselect"))
532                node->flags = 0;
533        else
534                node->flags = FTREE_NODE_SUBSCRIBABLE;
535        camel_url_free (url);
536
537        /* subscribed? */
538
539        if (camel_store_folder_subscribed (store, fi->full_name))
540                node->flags |= FTREE_NODE_SUBSCRIBED;
541
542        /* Copy strings */
543
544        node->uri_offset       = uri_offset;
545        node->full_name_offset = full_name_offset;
546
547        strcpy (node->data, fi->name);
548        strcpy (node->data + uri_offset, fi->url);
549        strcpy (node->data + full_name_offset, fi->full_name);
550
551        /* Done */
552
553        return node;
554}
555
556#define ftree_node_subscribable(node)  ( ((ftree_node *) (node))->flags & FTREE_NODE_SUBSCRIBABLE )
557#define ftree_node_subscribed(node)    ( ((ftree_node *) (node))->flags & FTREE_NODE_SUBSCRIBED )
558#define ftree_node_get_name(node)      ( ((ftree_node *) (node))->data )
559#define ftree_node_get_full_name(node) ( ((ftree_node *) (node))->data + ((ftree_node *) (node))->full_name_offset )
560#define ftree_node_get_uri(node)       ( ((ftree_node *) (node))->data + ((ftree_node *) (node))->uri_offset )
561
562/* ** Folder Tree Model **************************************************** */
563
564/* A subscribe or scan operation */
565
566typedef struct _ftree_op_data ftree_op_data;
567
568struct _ftree_op_data {
569        FolderETree *ftree;
570        ETreePath path;
571        ftree_node *data;
572        int handle;
573};
574
575
576/* ETreeModel functions */
577
578static int
579fe_column_count (ETreeModel *etm)
580{
581        return FOLDER_COL_LAST;
582}
583
584static void *
585fe_duplicate_value (ETreeModel *etm, int col, const void *val)
586{
587        return g_strdup (val);
588}
589
590static void
591fe_free_value (ETreeModel *etm, int col, void *val)
592{
593        g_free (val);
594}
595
596static void*
597fe_init_value (ETreeModel *etm, int col)
598{
599        return g_strdup ("");
600}
601
602static gboolean
603fe_value_is_empty (ETreeModel *etm, int col, const void *val)
604{
605        return !(val && *(char *)val);
606}
607
608static char *
609fe_value_to_string (ETreeModel *etm, int col, const void *val)
610{
611        return g_strdup (val);
612}
613
614static GdkPixbuf *
615fe_icon_at (ETreeModel *etree, ETreePath path)
616{
617        return NULL; /* XXX no icons for now */
618}
619
620static gpointer
621fe_root_value_at (FolderETree *ftree, int col)
622{
623        switch (col) {
624        case FOLDER_COL_NAME:
625                return ftree->service_name;
626        case FOLDER_COL_SUBSCRIBED:
627                return GINT_TO_POINTER (0);
628        default:
629                printf ("Oh no, unimplemented column %d in subscribe dialog\n", col);
630        }
631
632        return NULL;
633}
634
635static gpointer
636fe_real_value_at (FolderETree *ftree, int col, gpointer data)
637{
638        switch (col) {
639        case FOLDER_COL_NAME:
640                return ftree_node_get_name (data);
641        case FOLDER_COL_SUBSCRIBED:
642                if (ftree_node_subscribed (data))
643                        return GINT_TO_POINTER (1);
644                return GINT_TO_POINTER (0);
645        default:
646                printf ("Oh no, unimplemented column %d in subscribe dialog\n", col);
647        }
648       
649        return NULL;
650}
651
652static void *
653fe_value_at (ETreeModel *etree, ETreePath path, int col)
654{
655        FolderETree *ftree = (FolderETree *) etree;
656        gpointer node_data;
657       
658        if (path == ftree->root)
659                return fe_root_value_at (ftree, col);
660
661        node_data = e_tree_memory_node_get_data (E_TREE_MEMORY (etree), path);
662        return fe_real_value_at (ftree, col, node_data);
663}
664
665static void
666fe_set_value_at (ETreeModel *etree, ETreePath path, int col, const void *val)
667{
668        /* nothing */
669}
670
671static gboolean
672fe_return_false (void)
673{
674        return FALSE;
675}
676
677static gint
678fe_sort_folder (ETreeMemory *etmm, ETreePath left, ETreePath right, gpointer user_data)
679{
680        ftree_node *n_left, *n_right;
681
682        n_left = e_tree_memory_node_get_data (etmm, left);
683        n_right = e_tree_memory_node_get_data (etmm, right);
684
685        return g_strcasecmp (ftree_node_get_name (n_left), ftree_node_get_name (n_right));
686}
687
688/* scanning */
689
690static void
691fe_got_children (CamelStore *store, char *prefix, CamelFolderInfo *info, gpointer data)
692{
693        ftree_op_data *closure = (ftree_op_data *) data;
694
695        if (!info) /* cancelled */
696                return;
697
698        if (!prefix)
699                prefix = "";
700
701        for ( ; info; info = info->sibling) {
702                ETreePath   child_path;
703                ftree_node *node;
704
705                if (strcmp (info->full_name, prefix) == 0)
706                        continue;
707
708                node = ftree_node_new (store, info);
709                child_path = e_tree_memory_node_insert (E_TREE_MEMORY (closure->ftree),
710                                                        closure->path,
711                                                        0,
712                                                        node);
713                e_tree_memory_sort_node (E_TREE_MEMORY (closure->ftree),
714                                         closure->path,
715                                         fe_sort_folder,
716                                         NULL);
717        }
718
719        if (closure->data)
720                closure->data->flags |= FTREE_NODE_GOT_CHILDREN;
721
722        g_hash_table_remove (closure->ftree->scan_ops, closure->path);
723        g_free (closure);
724}
725
726static void
727fe_check_for_children (FolderETree *ftree, ETreePath path)
728{
729        ftree_op_data *closure;
730        ftree_node *node;
731        char *prefix;
732
733        node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path);
734
735        /* have we already gotten these children? */
736        if (node->flags & FTREE_NODE_GOT_CHILDREN)
737                return;
738
739        /* or we're loading them right now? */
740        if (g_hash_table_lookup (ftree->scan_ops, path))
741                return;
742
743        /* figure out our search prefix */
744        if (path == ftree->root)
745                prefix = ftree->search;
746        else
747                prefix = ftree_node_get_full_name (node);
748
749        closure = g_new (ftree_op_data, 1);
750        closure->ftree = ftree;
751        closure->path = path;
752        closure->data = node;
753        closure->handle = -1;
754
755        g_hash_table_insert (ftree->scan_ops, path, closure);
756
757        /* FIXME. Tiny race possiblity I guess. */
758
759        closure->handle = subscribe_get_short_folderinfo (ftree, prefix, fe_got_children, closure);
760}
761
762static void
763fe_create_root_node (FolderETree *ftree)
764{
765        ftree_node *node;
766
767        node = ftree_node_new_root (ftree->search);
768        ftree->root = e_tree_memory_node_insert (E_TREE_MEMORY(ftree), NULL, 0, node);
769        fe_check_for_children (ftree, ftree->root);
770}
771
772static ETreePath
773fe_get_first_child (ETreeModel *model, ETreePath path)
774{
775        ETreePath child_path;
776
777        child_path = E_TREE_MODEL_CLASS (folder_etree_parent_class)->get_first_child (model, path);
778        if (child_path)
779                fe_check_for_children ((FolderETree *) model, child_path);
780        else
781                fe_check_for_children ((FolderETree *) model, path);
782        return child_path;
783}
784
785/* subscribing */
786
787static char *
788fe_node_to_shell_path (ftree_node *node)
789{
790        char *path = NULL;
791        int name_len, full_name_len;
792
793        name_len = strlen (ftree_node_get_name (node));
794        full_name_len = strlen (ftree_node_get_full_name (node));
795       
796        if (name_len != full_name_len) {
797                char *full_name;
798                char *iter;
799                char sep;
800       
801                /* so, we don't know the heirarchy separator. But
802                 * full_name = blahXblahXname, where X = separator
803                 * and name = .... name. So we can determine it.
804                 * (imap_store->dir_sep isn't really private, I guess,
805                 * so we could use that if we had the store. But also
806                 * we don't "know" that it is an IMAP store anyway.)
807                 */
808
809                full_name = ftree_node_get_full_name (node);
810                sep = full_name[full_name_len - (name_len + 1)];
811
812                if (sep != '/') {
813                        path = g_malloc (full_name_len + 2);
814                        path[0] = '/';
815                        strcpy (path + 1, full_name);
816                        while ((iter = strchr (path, sep)) != NULL)
817                                *iter = '/';
818                }
819        }
820
821        if (!path)
822                path = g_strdup_printf ("/%s", ftree_node_get_full_name (node));
823
824        return path;
825}
826
827static void
828fe_done_subscribing (const char *full_name, const char *name, gboolean subscribe, gboolean success, gpointer user_data)
829{
830        ftree_op_data *closure = (ftree_op_data *) user_data;
831
832        if (success && closure->handle != -1) {
833                char *path;
834
835                path = fe_node_to_shell_path (closure->data);
836
837                if (subscribe) {
838                        closure->data->flags |= FTREE_NODE_SUBSCRIBED;
839                        recursive_add_folder (closure->ftree->e_storage,
840                                              path, name,
841                                              ftree_node_get_uri (closure->data));
842                } else {
843                        closure->data->flags &= ~FTREE_NODE_SUBSCRIBED;
844
845                        /* FIXME: recursively remove folder as well? Possible? */
846                        evolution_storage_removed_folder (closure->ftree->e_storage, path);
847                }
848
849                g_free (path);
850                e_tree_model_node_data_changed (E_TREE_MODEL (closure->ftree), closure->path);
851        }
852
853        if (closure->handle != -1)
854                g_hash_table_remove (closure->ftree->subscribe_ops, closure->path);
855
856        g_free (closure);
857}
858
859/* cleanup */
860
861static gboolean
862fe_cancel_op_foreach (gpointer key, gpointer value, gpointer user_data)
863{
864        /*FolderETree   *ftree = (FolderETree *) user_data;*/
865        ftree_op_data *closure = (ftree_op_data *) value;
866
867        if (closure->handle != -1)
868                mail_msg_cancel (closure->handle);
869
870        closure->handle = -1;
871
872        return TRUE;
873}
874
875static void
876fe_kill_current_tree (FolderETree *ftree)
877{
878        g_hash_table_foreach_remove (ftree->scan_ops, fe_cancel_op_foreach, ftree);
879        g_assert (g_hash_table_size (ftree->scan_ops) == 0);
880}
881
882static void
883fe_destroy (GtkObject *obj)
884{
885        FolderETree *ftree = (FolderETree *) (obj);
886
887        fe_kill_current_tree (ftree);
888
889        g_hash_table_foreach_remove (ftree->subscribe_ops, fe_cancel_op_foreach, ftree);
890       
891        g_hash_table_destroy (ftree->scan_ops);
892        g_hash_table_destroy (ftree->subscribe_ops);
893
894        camel_object_unref (CAMEL_OBJECT (ftree->store));
895        bonobo_object_unref (BONOBO_OBJECT (ftree->e_storage));
896
897        g_free (ftree->search);
898        g_free (ftree->service_name);
899}
900
901typedef gboolean (*bool_func_1) (ETreeModel *, ETreePath, int);
902typedef gboolean (*bool_func_2) (ETreeModel *);
903
904static void
905folder_etree_class_init (GtkObjectClass *klass)
906{
907        ETreeModelClass  *etree_model_class = E_TREE_MODEL_CLASS (klass);
908
909        folder_etree_parent_class = gtk_type_class (E_TREE_MEMORY_TYPE);
910
911        klass->destroy                          = fe_destroy;
912
913        etree_model_class->value_at             = fe_value_at;
914        etree_model_class->set_value_at         = fe_set_value_at;
915        etree_model_class->column_count         = fe_column_count;
916        etree_model_class->duplicate_value      = fe_duplicate_value;
917        etree_model_class->free_value           = fe_free_value;
918        etree_model_class->initialize_value     = fe_init_value;
919        etree_model_class->value_is_empty       = fe_value_is_empty;
920        etree_model_class->value_to_string      = fe_value_to_string;
921        etree_model_class->icon_at              = fe_icon_at;
922        etree_model_class->is_editable          = (bool_func_1) fe_return_false;
923        etree_model_class->has_save_id          = (bool_func_2) fe_return_false;
924        etree_model_class->has_get_node_by_id   = (bool_func_2) fe_return_false;
925        etree_model_class->get_first_child      = fe_get_first_child;
926}
927
928static void
929folder_etree_init (GtkObject *object)
930{
931        FolderETree *ftree = (FolderETree *) object;
932
933        e_tree_memory_set_node_destroy_func (E_TREE_MEMORY (ftree), (GFunc) g_free, ftree);
934
935        ftree->scan_ops = g_hash_table_new (g_direct_hash, g_direct_equal);
936        ftree->subscribe_ops = g_hash_table_new (g_direct_hash, g_direct_equal);
937
938        ftree->search = g_strdup ("");
939}
940
941static FolderETree *
942folder_etree_construct (FolderETree *ftree,
943                        CamelStore  *store)
944{
945        e_tree_memory_construct (E_TREE_MEMORY (ftree));
946       
947        ftree->store = store;
948        camel_object_ref (CAMEL_OBJECT (store));
949       
950        ftree->service_name = camel_service_get_name (CAMEL_SERVICE (store), FALSE);
951       
952        ftree->e_storage = mail_lookup_storage (store); /* this gives us a ref */
953
954        fe_create_root_node (ftree);
955
956        return ftree;
957}
958
959static
960E_MAKE_TYPE (folder_etree, "FolderETree", FolderETree, folder_etree_class_init, folder_etree_init, E_TREE_MEMORY_TYPE);
961
962/* public */
963
964static FolderETree *
965folder_etree_new (CamelStore *store)
966{
967        FolderETree *ftree;
968
969        ftree = gtk_type_new (folder_etree_get_type());
970        ftree = folder_etree_construct (ftree, store);
971        return ftree;
972}
973
974static void
975folder_etree_clear_tree (FolderETree *ftree)
976{
977        e_tree_memory_freeze (E_TREE_MEMORY (ftree));
978        e_tree_memory_node_remove (E_TREE_MEMORY (ftree), ftree->root);
979        fe_create_root_node (ftree);
980        e_tree_memory_thaw (E_TREE_MEMORY (ftree));
981}
982
983static void
984folder_etree_set_search (FolderETree *ftree, const char *search)
985{
986        if (!strcmp (search, ftree->search))
987                return;
988
989        g_free (ftree->search);
990        ftree->search = g_strdup (search);
991       
992        folder_etree_clear_tree (ftree);
993}
994
995
996static int
997folder_etree_path_set_subscription (FolderETree *ftree, ETreePath path, gboolean subscribe)
998{
999        ftree_op_data *closure;
1000        ftree_node    *node;
1001
1002        /* already in progress? */
1003
1004        if (g_hash_table_lookup (ftree->subscribe_ops, path))
1005                return 0;
1006
1007        /* noselect? */
1008
1009        node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path);
1010
1011        if (!ftree_node_subscribable (node))
1012                return -1;
1013
1014        /* noop? */
1015
1016        /* uh, this should be a not XOR or something */
1017        if ((ftree_node_subscribed (node) && subscribe) ||
1018            (!ftree_node_subscribed (node) && !subscribe))
1019                return 0;
1020
1021        closure         = g_new (ftree_op_data, 1);
1022        closure->ftree  = ftree;
1023        closure->path   = path;
1024        closure->data   = node;
1025        closure->handle = -1;
1026
1027        g_hash_table_insert (ftree->subscribe_ops, path, closure);
1028
1029        closure->handle = subscribe_do_subscribe_folder (ftree->store,
1030                                                         ftree_node_get_full_name (node),
1031                                                         ftree_node_get_name (node),
1032                                                         subscribe,
1033                                                         fe_done_subscribing,
1034                                                         closure);
1035        return 0;
1036}
1037
1038static int
1039folder_etree_path_toggle_subscription (FolderETree *ftree, ETreePath path)
1040{
1041        ftree_node *node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path);
1042
1043        if (ftree_node_subscribed (node))
1044                return folder_etree_path_set_subscription (ftree, path, FALSE);
1045        else
1046                return folder_etree_path_set_subscription (ftree, path, TRUE);
1047}
1048
1049/* ** StoreData ************************************************************ */
1050
1051typedef struct _StoreData StoreData;
1052
1053typedef void (*StoreDataStoreFunc) (StoreData *, CamelStore *, gpointer);
1054
1055struct _StoreData {
1056        int refcount;
1057        char *uri;
1058       
1059        FolderETree *ftree;
1060        CamelStore *store;
1061       
1062        int request_id;
1063       
1064        GtkWidget *widget;
1065        StoreDataStoreFunc store_func;
1066        gpointer store_data;
1067};
1068
1069static StoreData *
1070store_data_new (const char *uri)
1071{
1072        StoreData *sd;
1073       
1074        sd = g_new0 (StoreData, 1);
1075        sd->refcount = 1;
1076        sd->uri = g_strdup (uri);
1077       
1078        return sd;
1079}
1080
1081static void
1082store_data_free (StoreData *sd)
1083{
1084        if (sd->request_id)
1085                mail_msg_cancel (sd->request_id);
1086       
1087        if (sd->widget)
1088                gtk_object_unref (GTK_OBJECT (sd->widget));
1089       
1090        if (sd->ftree)
1091                gtk_object_unref (GTK_OBJECT (sd->ftree));
1092       
1093        if (sd->store)
1094                camel_object_unref (CAMEL_OBJECT (sd->store));
1095       
1096        g_free (sd->uri);
1097        g_free (sd);
1098}
1099
1100static void
1101store_data_ref (StoreData *sd)
1102{
1103        sd->refcount++;
1104}
1105
1106static void
1107store_data_unref (StoreData *sd)
1108{
1109        if (sd->refcount <= 1) {
1110                store_data_free (sd);
1111        } else {
1112                sd->refcount--;
1113        }
1114}
1115
1116static void
1117sd_got_store (char *uri, CamelStore *store, gpointer user_data)
1118{
1119        StoreData *sd = (StoreData *) user_data;
1120       
1121        sd->store = store;
1122       
1123        if (store) /* we can have exceptions getting the store... server is down, eg */
1124                camel_object_ref (CAMEL_OBJECT (sd->store));
1125       
1126        /* uh, so we might have a problem if this operation is cancelled. Unsure. */
1127        sd->request_id = 0;
1128       
1129        if (sd->store_func)
1130                (sd->store_func) (sd, sd->store, sd->store_data);
1131       
1132        store_data_unref (sd);
1133}
1134
1135static void
1136store_data_async_get_store (StoreData *sd, StoreDataStoreFunc func, gpointer user_data)
1137{
1138        if (sd->request_id) {
1139                d(printf ("Already loading store, nooping\n"));
1140                return;
1141        }
1142       
1143        if (sd->store) {
1144                /* um, is this the best behavior? */
1145                func (sd, sd->store, user_data);
1146                return;
1147        }
1148       
1149        sd->store_func = func;
1150        sd->store_data = user_data;
1151        store_data_ref (sd);
1152        sd->request_id = mail_get_store (sd->uri, sd_got_store, sd);
1153}
1154
1155static void
1156store_data_cancel_get_store (StoreData *sd)
1157{
1158        g_return_if_fail (sd->request_id);
1159
1160        mail_msg_cancel (sd->request_id);
1161        sd->request_id = 0;
1162}
1163
1164static void
1165sd_toggle_cb (ETree *tree, int row, ETreePath path, int col, GdkEvent *event, gpointer user_data)
1166{
1167        StoreData *sd = (StoreData *) user_data;
1168
1169        folder_etree_path_toggle_subscription (sd->ftree, path);
1170}
1171
1172static GtkWidget *
1173store_data_get_widget (StoreData *sd)
1174{
1175        GtkWidget *tree;
1176
1177        if (!sd->store) {
1178                d(printf ("store data can't get widget before getting store.\n"));
1179                return NULL;
1180        }
1181
1182        if (sd->widget)
1183                return sd->widget;
1184
1185        sd->ftree = folder_etree_new (sd->store);
1186
1187        /* You annoy me, etree! */
1188        tree = gtk_widget_new (E_TREE_SCROLLED_TYPE,
1189                               "hadjustment", NULL,
1190                               "vadjustment", NULL,
1191                               NULL);
1192
1193        tree = (GtkWidget *) e_tree_scrolled_construct_from_spec_file (E_TREE_SCROLLED (tree),
1194                                                                       E_TREE_MODEL (sd->ftree),
1195                                                                       subscribe_get_global_extras (),
1196                                                                       EVOLUTION_ETSPECDIR "/subscribe-dialog.etspec",
1197                                                                       NULL);
1198        e_tree_root_node_set_visible (e_tree_scrolled_get_tree(E_TREE_SCROLLED(tree)), TRUE);
1199        gtk_signal_connect (GTK_OBJECT (e_tree_scrolled_get_tree(E_TREE_SCROLLED (tree))),
1200                            "double_click", GTK_SIGNAL_FUNC (sd_toggle_cb), sd);
1201
1202        gtk_object_unref (GTK_OBJECT (global_extras));
1203
1204        sd->widget = tree;
1205        gtk_object_ref (GTK_OBJECT (sd->widget));
1206
1207        return sd->widget;
1208}
1209
1210typedef struct _selection_closure {
1211        StoreData *sd;
1212        enum { SET, CLEAR, TOGGLE } mode;
1213} selection_closure;
1214
1215static void
1216sd_subscribe_folder_foreach (int model_row, gpointer closure)
1217{
1218        selection_closure *sc   = (selection_closure *) closure;
1219        StoreData         *sd   = sc->sd;
1220        ETree             *tree = e_tree_scrolled_get_tree(E_TREE_SCROLLED(sd->widget));
1221        ETreePath          path = e_tree_node_at_row (tree, model_row);
1222
1223        /* ignore results */
1224        switch (sc->mode) {
1225        case SET:
1226                folder_etree_path_set_subscription (sd->ftree, path, TRUE);
1227                break;
1228        case CLEAR:
1229                folder_etree_path_set_subscription (sd->ftree, path, FALSE);
1230                break;
1231        case TOGGLE:
1232                folder_etree_path_toggle_subscription (sd->ftree, path);
1233                break;
1234        }
1235}
1236
1237static void
1238store_data_selection_set_subscription (StoreData *sd, gboolean subscribe)
1239{
1240        selection_closure sc;
1241        ETree *tree;
1242       
1243        sc.sd = sd;
1244        if (subscribe)
1245                sc.mode = SET;
1246        else
1247                sc.mode = CLEAR;
1248
1249        tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (sd->widget));
1250        e_tree_selected_row_foreach (tree, sd_subscribe_folder_foreach, &sc);
1251}
1252
1253#ifdef NEED_TOGGLE_SELECTION
1254static void
1255store_data_selection_toggle_subscription (StoreData *sd)
1256{
1257        selection_closure  sc;
1258        ETree             *tree;
1259       
1260        sc.sd   = sd;
1261        sc.mode = TOGGLE;
1262
1263        tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (sd->widget));
1264        e_tree_selected_row_foreach (tree, sd_subscribe_folder_foreach, &sc);
1265}
1266#endif
1267
1268static gboolean
1269store_data_mid_request (StoreData *sd)
1270{
1271        return (gboolean) sd->request_id;
1272}
1273
1274/* ** yaay, SubscribeDialog ******************************************************* */
1275
1276#define PARENT_TYPE (gtk_object_get_type ())
1277
1278#ifdef JUST_FOR_TRANSLATORS
1279static char *str = N_("Folder");
1280#endif
1281
1282#define STORE_DATA_KEY   "store-data"
1283
1284struct _SubscribeDialogPrivate {
1285        GladeXML  *xml;
1286        GList     *store_list;
1287
1288        StoreData *current_store;
1289        GtkWidget *current_widget;
1290
1291        GtkWidget *default_widget;
1292        GtkWidget *none_item;
1293        GtkWidget *search_entry;
1294        GtkWidget *hbox;
1295        GtkWidget *filter_radio, *all_radio;
1296        GtkWidget *sub_button, *unsub_button, *refresh_button;
1297};
1298
1299static GtkObjectClass *subscribe_dialog_parent_class;
1300
1301static void
1302sc_refresh_pressed (GtkWidget *widget, gpointer user_data)
1303{
1304        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1305
1306        if (sc->priv->current_store)
1307                folder_etree_clear_tree (sc->priv->current_store->ftree);
1308}
1309
1310static void
1311sc_search_activated (GtkWidget *widget, gpointer user_data)
1312{
1313        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1314        StoreData       *store = sc->priv->current_store;
1315        char            *search;
1316
1317        if (!store)
1318                return;
1319
1320        search = e_utf8_gtk_entry_get_text (GTK_ENTRY (widget));
1321        folder_etree_set_search (store->ftree, search);
1322}
1323
1324static void
1325sc_subscribe_pressed (GtkWidget *widget, gpointer user_data)
1326{
1327        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1328        StoreData       *store = sc->priv->current_store;
1329
1330        if (!store)
1331                return;
1332
1333        store_data_selection_set_subscription (store, TRUE);
1334}
1335
1336static void
1337sc_unsubscribe_pressed (GtkWidget *widget, gpointer user_data)
1338{
1339        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1340        StoreData *store = sc->priv->current_store;
1341
1342        if (!store)
1343                return;
1344
1345        store_data_selection_set_subscription (store, FALSE);
1346}
1347
1348static void
1349sc_all_toggled (GtkWidget *widget, gpointer user_data)
1350{
1351        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1352        StoreData       *store = sc->priv->current_store;
1353
1354        if (!store)
1355                return;
1356
1357        if (GTK_TOGGLE_BUTTON (widget)->active) {
1358                gtk_widget_set_sensitive (sc->priv->search_entry, FALSE);
1359                folder_etree_set_search (store->ftree, "");
1360        }
1361}
1362
1363static void
1364sc_filter_toggled (GtkWidget *widget, gpointer user_data)
1365{
1366        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1367        StoreData       *store = sc->priv->current_store;
1368
1369        if (!store)
1370                return;
1371
1372        if (GTK_TOGGLE_BUTTON (widget)->active) {
1373                gtk_widget_set_sensitive (sc->priv->search_entry, TRUE);
1374                sc_search_activated (sc->priv->search_entry, sc);
1375        }
1376}
1377
1378static void
1379populate_store_foreach (MailConfigService *service, SubscribeDialog *sc)
1380{
1381        StoreData            *sd;
1382
1383        if (service->url == NULL || service->enabled == FALSE)
1384                return;
1385
1386        sd = store_data_new (service->url);
1387        sc->priv->store_list = g_list_prepend (sc->priv->store_list, sd);
1388}
1389
1390static void
1391kill_default_view (SubscribeDialog *sc)
1392{
1393        gtk_widget_hide (sc->priv->none_item);
1394
1395        /* the entry will be set sensitive when one of the
1396         * radio buttons is activated, if necessary. */
1397
1398        gtk_widget_set_sensitive (sc->priv->all_radio, TRUE);
1399        gtk_widget_set_sensitive (sc->priv->filter_radio, TRUE);
1400        gtk_widget_set_sensitive (sc->priv->sub_button, TRUE);
1401        gtk_widget_set_sensitive (sc->priv->unsub_button, TRUE);
1402        gtk_widget_set_sensitive (sc->priv->refresh_button, TRUE);
1403}
1404
1405static void
1406sc_selection_changed (GtkObject *obj, gpointer user_data)
1407{
1408        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1409        gboolean sensitive;
1410
1411        if (e_selection_model_selected_count (E_SELECTION_MODEL (obj)))
1412                sensitive = TRUE;
1413        else
1414                sensitive = FALSE;
1415
1416        gtk_widget_set_sensitive (sc->priv->sub_button, sensitive);
1417        gtk_widget_set_sensitive (sc->priv->unsub_button, sensitive);
1418}
1419
1420static void
1421menu_item_selected (GtkMenuItem *item, gpointer user_data)
1422{
1423        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1424        StoreData       *sd = gtk_object_get_data (GTK_OBJECT (item), STORE_DATA_KEY);
1425
1426        g_return_if_fail (sd);
1427
1428        if (sd->widget == NULL) {
1429                GtkWidget *widget;
1430                ESelectionModel *esm;
1431                ETree *tree;
1432
1433                widget = store_data_get_widget (sd);
1434                gtk_box_pack_start (GTK_BOX (sc->priv->hbox), widget, TRUE, TRUE, 0);
1435
1436                tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (widget));
1437                esm = e_tree_get_selection_model (tree);
1438                gtk_signal_connect (GTK_OBJECT (esm), "selection_changed", sc_selection_changed, sc);
1439                sc_selection_changed ((GtkObject *)esm, sc);
1440        }
1441
1442        if (sc->priv->current_widget == sc->priv->default_widget)
1443                kill_default_view (sc);
1444
1445        gtk_widget_hide (sc->priv->current_widget);
1446        gtk_widget_show (sd->widget);
1447        sc->priv->current_widget = sd->widget;
1448        sc->priv->current_store  = sd;
1449
1450        if (*sd->ftree->search) {
1451                e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), sd->ftree->search);
1452                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sc->priv->filter_radio), TRUE);
1453        } else {
1454                e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), "");
1455                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sc->priv->all_radio), TRUE);
1456        }
1457}
1458
1459static void
1460dummy_item_selected (GtkMenuItem *item, gpointer user_data)
1461{
1462        SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
1463
1464        gtk_widget_hide (sc->priv->current_widget);
1465        gtk_widget_show (sc->priv->default_widget);
1466        sc->priv->current_widget = sc->priv->default_widget;
1467        sc->priv->current_store  = NULL;
1468
1469        e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), "");
1470}
1471
1472/* wonderful */
1473
1474static void
1475got_sd_store (StoreData *sd, CamelStore *store, gpointer data)
1476{
1477        if (store && camel_store_supports_subscriptions (store))
1478                gtk_widget_show (GTK_WIDGET (data));
1479}
1480
1481/* FIXME: if there aren't any stores that are subscribable, the option
1482 * menu will only have the "No server selected" item and the user will
1483 * be confused. */
1484
1485static void
1486populate_store_list (SubscribeDialog *sc)
1487{
1488        const GSList *news;
1489        GSList       *sources;
1490        GList        *iter;
1491        GtkWidget    *menu;
1492        GtkWidget    *omenu;
1493
1494        sources = mail_config_get_sources ();
1495        g_slist_foreach (sources, (GFunc) populate_store_foreach, sc);
1496        g_slist_free (sources);
1497       
1498        news = mail_config_get_news ();
1499        g_slist_foreach ((GSList *) news, (GFunc) populate_store_foreach, sc);
1500
1501        menu = gtk_menu_new ();
1502
1503        for (iter = sc->priv->store_list; iter; iter = iter->next) {
1504                GtkWidget *item;
1505                CamelURL *url;
1506                char *string;
1507
1508                url = camel_url_new (((StoreData *) iter->data)->uri, NULL);
1509                string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
1510                camel_url_free (url);
1511                item = gtk_menu_item_new_with_label (string);
1512                store_data_async_get_store (iter->data, got_sd_store, item);
1513                gtk_object_set_data (GTK_OBJECT (item), STORE_DATA_KEY, iter->data);
1514                gtk_signal_connect (GTK_OBJECT (item), "activate", menu_item_selected, sc);
1515                g_free (string);
1516
1517                gtk_menu_prepend (GTK_MENU (menu), item);
1518        }
1519
1520        sc->priv->none_item = gtk_menu_item_new_with_label (_("No server has been selected"));
1521        gtk_signal_connect (GTK_OBJECT (sc->priv->none_item), "activate", dummy_item_selected, sc);
1522        gtk_widget_show (sc->priv->none_item);
1523        gtk_menu_prepend (GTK_MENU (menu), sc->priv->none_item);
1524
1525        gtk_widget_show (menu);
1526
1527        omenu = glade_xml_get_widget (sc->priv->xml, "store_menu");
1528        gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
1529}
1530
1531static void
1532subscribe_dialog_destroy (GtkObject *object)
1533{
1534        SubscribeDialog *sc;
1535        GList *iter;
1536       
1537        sc = SUBSCRIBE_DIALOG (object);
1538       
1539        for (iter = sc->priv->store_list; iter; iter = iter->next) {
1540                StoreData *data = iter->data;
1541               
1542                if (store_data_mid_request (data))
1543                        store_data_cancel_get_store (data);
1544               
1545                data->store_func = NULL;
1546               
1547                store_data_unref (data);
1548        }
1549       
1550        g_list_free (sc->priv->store_list);
1551       
1552        gtk_object_unref (GTK_OBJECT (sc->priv->xml));
1553
1554        g_free (sc->priv);
1555
1556        subscribe_dialog_parent_class->destroy (object);
1557}
1558
1559static void
1560subscribe_dialog_class_init (GtkObjectClass *object_class)
1561{
1562        object_class->destroy = subscribe_dialog_destroy;
1563
1564        subscribe_dialog_parent_class = gtk_type_class (PARENT_TYPE);
1565}
1566
1567static void
1568subscribe_dialog_init (GtkObject *object)
1569{
1570        SubscribeDialog *sc = SUBSCRIBE_DIALOG (object);
1571
1572        sc->priv = g_new0 (SubscribeDialogPrivate, 1);
1573}
1574
1575static GtkWidget *
1576sc_create_default_widget (void)
1577{
1578        GtkWidget *label;
1579        GtkWidget *viewport;
1580
1581        label = gtk_label_new (_("Please select a server."));
1582        gtk_widget_show (label);
1583
1584        viewport = gtk_viewport_new (NULL, NULL);
1585        gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_IN);
1586        gtk_container_add (GTK_CONTAINER (viewport), label);
1587
1588        return viewport;
1589}
1590
1591static void
1592subscribe_dialog_construct (GtkObject *object)
1593{
1594        SubscribeDialog *sc = SUBSCRIBE_DIALOG (object);
1595       
1596        /* Load the XML */
1597        sc->priv->xml            = glade_xml_new (EVOLUTION_GLADEDIR "/subscribe-dialog.glade", NULL);
1598       
1599        sc->app                  = glade_xml_get_widget (sc->priv->xml, "Manage Subscriptions");
1600        sc->priv->hbox           = glade_xml_get_widget (sc->priv->xml, "tree_box");
1601        sc->priv->search_entry   = glade_xml_get_widget (sc->priv->xml, "search_entry");
1602        sc->priv->filter_radio   = glade_xml_get_widget (sc->priv->xml, "filter_radio");
1603        sc->priv->all_radio      = glade_xml_get_widget (sc->priv->xml, "all_radio");
1604        sc->priv->sub_button     = glade_xml_get_widget (sc->priv->xml, "subscribe_button");
1605        sc->priv->unsub_button   = glade_xml_get_widget (sc->priv->xml, "unsubscribe_button");
1606        sc->priv->refresh_button = glade_xml_get_widget (sc->priv->xml, "refresh_button");
1607       
1608        /* create default view */
1609        sc->priv->default_widget = sc_create_default_widget();
1610        sc->priv->current_widget = sc->priv->default_widget;
1611        gtk_box_pack_start (GTK_BOX (sc->priv->hbox), sc->priv->default_widget, TRUE, TRUE, 0);
1612        gtk_widget_show (sc->priv->default_widget);
1613       
1614        gtk_widget_set_sensitive (sc->priv->all_radio, FALSE);
1615        gtk_widget_set_sensitive (sc->priv->filter_radio, FALSE);
1616        gtk_widget_set_sensitive (sc->priv->search_entry, FALSE);
1617        gtk_widget_set_sensitive (sc->priv->sub_button, FALSE);
1618        gtk_widget_set_sensitive (sc->priv->unsub_button, FALSE);
1619        gtk_widget_set_sensitive (sc->priv->refresh_button, FALSE);
1620       
1621        /* hook up some signals */
1622        gtk_signal_connect (GTK_OBJECT (sc->priv->search_entry), "activate", sc_search_activated, sc);
1623        gtk_signal_connect (GTK_OBJECT (sc->priv->sub_button), "clicked", sc_subscribe_pressed, sc);
1624        gtk_signal_connect (GTK_OBJECT (sc->priv->unsub_button), "clicked", sc_unsubscribe_pressed, sc);
1625        gtk_signal_connect (GTK_OBJECT (sc->priv->refresh_button), "clicked", sc_refresh_pressed, sc);
1626        gtk_signal_connect (GTK_OBJECT (sc->priv->all_radio), "toggled", sc_all_toggled, sc);
1627        gtk_signal_connect (GTK_OBJECT (sc->priv->filter_radio), "toggled", sc_filter_toggled, sc);
1628       
1629        /* Get the list of stores */
1630        populate_store_list (sc);
1631}
1632
1633GtkObject *
1634subscribe_dialog_new (void)
1635{
1636        SubscribeDialog *subscribe_dialog;
1637       
1638        subscribe_dialog = gtk_type_new (SUBSCRIBE_DIALOG_TYPE);
1639        subscribe_dialog_construct (GTK_OBJECT (subscribe_dialog));
1640       
1641        return GTK_OBJECT (subscribe_dialog);
1642}
1643
1644E_MAKE_TYPE (subscribe_dialog, "SubscribeDialog", SubscribeDialog, subscribe_dialog_class_init, subscribe_dialog_init, PARENT_TYPE);
Note: See TracBrowser for help on using the repository browser.