sync with sylpheed 0.4.67cvs7-8
[claws.git] / src / folderview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2000 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkwidget.h>
25 #include <gtk/gtkscrolledwindow.h>
26 #include <gtk/gtkctree.h>
27 #include <gtk/gtkcontainer.h>
28 #include <gtk/gtkclist.h>
29 #include <gtk/gtkstyle.h>
30 #include <gtk/gtksignal.h>
31 #include <gtk/gtkmain.h>
32 #include <gtk/gtkstatusbar.h>
33 #include <gtk/gtkmenu.h>
34 #include <gtk/gtkmenuitem.h>
35 #include <gtk/gtkitemfactory.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39
40 #include "intl.h"
41 #include "main.h"
42 #include "mainwindow.h"
43 #include "folderview.h"
44 #include "summaryview.h"
45 #include "inputdialog.h"
46 #include "manage_window.h"
47 #include "alertpanel.h"
48 #include "menu.h"
49 #include "procmsg.h"
50 #include "utils.h"
51 #include "gtkutils.h"
52 #include "prefs_common.h"
53 #include "prefs_account.h"
54 #include "account.h"
55 #include "folder.h"
56 #include "grouplist_dialog.h"
57
58 #include "pixmaps/inbox.xpm"
59 #include "pixmaps/outbox.xpm"
60 #include "pixmaps/dir-close.xpm"
61 #include "pixmaps/dir-open.xpm"
62 #include "pixmaps/trash.xpm"
63
64 typedef enum
65 {
66         COL_FOLDER      = 0,
67         COL_NEW         = 1,
68         COL_UNREAD      = 2,
69         COL_TOTAL       = 3
70 } FolderColumnPos;
71
72 #define N_FOLDER_COLS           4
73 #define COL_FOLDER_WIDTH        150
74 #define COL_NUM_WIDTH           32
75
76 #define STATUSBAR_PUSH(mainwin, str) \
77 { \
78         gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
79                            mainwin->folderview_cid, str); \
80         gtkut_widget_wait_for_draw(mainwin->hbox_stat); \
81 }
82
83 #define STATUSBAR_POP(mainwin) \
84 { \
85         gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
86                           mainwin->folderview_cid); \
87 }
88
89 static GList *folderview_list = NULL;
90
91 static GdkFont *normalfont;
92 static GdkFont *boldfont;
93
94 static GdkPixmap *inboxxpm;
95 static GdkBitmap *inboxxpmmask;
96 static GdkPixmap *outboxxpm;
97 static GdkBitmap *outboxxpmmask;
98 static GdkPixmap *folderxpm;
99 static GdkBitmap *folderxpmmask;
100 static GdkPixmap *folderopenxpm;
101 static GdkBitmap *folderopenxpmmask;
102 static GdkPixmap *trashxpm;
103 static GdkBitmap *trashxpmmask;
104
105 static void folderview_select_node       (FolderView    *folderview,
106                                           GtkCTreeNode  *node);
107 static void folderview_set_folders       (FolderView    *folderview);
108 static void folderview_sort_folders      (FolderView    *folderview,
109                                           GtkCTreeNode  *root,
110                                           Folder        *folder);
111 static void folderview_append_folder     (FolderView    *folderview,
112                                           Folder        *folder);
113 static void folderview_update_node       (FolderView    *folderview,
114                                           GtkCTreeNode  *node);
115
116 static GtkCTreeNode *folderview_find_by_name    (GtkCTree       *ctree,
117                                                  GtkCTreeNode   *node,
118                                                  const gchar    *name);
119
120 static gint folderview_compare_name     (gconstpointer   a,
121                                          gconstpointer   b);
122
123 /* callback functions */
124 static void folderview_button_pressed   (GtkWidget      *ctree,
125                                          GdkEventButton *event,
126                                          FolderView     *folderview);
127 static void folderview_button_released  (GtkWidget      *ctree,
128                                          GdkEventButton *event,
129                                          FolderView     *folderview);
130 static void folderview_key_pressed      (GtkWidget      *widget,
131                                          GdkEventKey    *event,
132                                          FolderView     *folderview);
133 static void folderview_selected         (GtkCTree       *ctree,
134                                          GtkCTreeNode   *row,
135                                          gint            column,
136                                          FolderView     *folderview);
137 static void folderview_tree_expanded    (GtkCTree       *ctree,
138                                          GtkCTreeNode   *node,
139                                          FolderView     *folderview);
140 static void folderview_tree_collapsed   (GtkCTree       *ctree,
141                                          GtkCTreeNode   *node,
142                                          FolderView     *folderview);
143 static void folderview_popup_close      (GtkMenuShell   *menu_shell,
144                                          FolderView     *folderview);
145 static void folderview_col_resized      (GtkCList       *clist,
146                                          gint            column,
147                                          gint            width,
148                                          FolderView     *folderview);
149
150 static void folderview_new_folder_cb    (FolderView     *folderview,
151                                          guint           action,
152                                          GtkWidget      *widget);
153 static void folderview_rename_folder_cb (FolderView     *folderview,
154                                          guint           action,
155                                          GtkWidget      *widget);
156 static void folderview_delete_folder_cb (FolderView     *folderview,
157                                          guint           action,
158                                          GtkWidget      *widget);
159 static void folderview_remove_mailbox_cb(FolderView     *folderview,
160                                          guint           action,
161                                          GtkWidget      *widget);
162
163 static void folderview_new_imap_folder_cb(FolderView    *folderview,
164                                           guint          action,
165                                           GtkWidget     *widget);
166 static void folderview_rm_imap_folder_cb (FolderView    *folderview,
167                                           guint          action,
168                                           GtkWidget     *widget);
169 static void folderview_rm_imap_server_cb (FolderView    *folderview,
170                                           guint          action,
171                                           GtkWidget     *widget);
172
173 static void folderview_new_news_group_cb(FolderView     *folderview,
174                                          guint           action,
175                                          GtkWidget      *widget);
176 static void folderview_rm_news_group_cb (FolderView     *folderview,
177                                          guint           action,
178                                          GtkWidget      *widget);
179 static void folderview_rm_news_server_cb(FolderView     *folderview,
180                                          guint           action,
181                                          GtkWidget      *widget);
182
183 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
184                                           GdkDragContext *context,
185                                           gint            x,
186                                           gint            y,
187                                           guint           time,
188                                           FolderView     *folderview);
189 static void folderview_drag_leave_cb     (GtkWidget        *widget,
190                                           GdkDragContext   *context,
191                                           guint             time,
192                                           FolderView       *folderview);
193 static void folderview_drag_received_cb  (GtkWidget        *widget,
194                                           GdkDragContext   *drag_context,
195                                           gint              x,
196                                           gint              y,
197                                           GtkSelectionData *data,
198                                           guint             info,
199                                           guint             time,
200                                           FolderView       *folderview);
201
202 static GtkItemFactoryEntry folderview_mail_popup_entries[] =
203 {
204         {N_("/Create _new folder..."),  NULL, folderview_new_folder_cb,    0, NULL},
205         {N_("/_Rename folder..."),      NULL, folderview_rename_folder_cb, 0, NULL},
206         {N_("/_Delete folder"),         NULL, folderview_delete_folder_cb, 0, NULL},
207         {N_("/---"),                    NULL, NULL, 0, "<Separator>"},
208         {N_("/Remove _mailbox"),        NULL, folderview_remove_mailbox_cb, 0, NULL},
209         {N_("/---"),                    NULL, NULL, 0, "<Separator>"},
210         {N_("/_Property..."),           NULL, NULL, 0, NULL}
211 };
212
213 static GtkItemFactoryEntry folderview_imap_popup_entries[] =
214 {
215         {N_("/Create _new folder..."),  NULL, folderview_new_imap_folder_cb, 0, NULL},
216         {N_("/_Rename folder..."),      NULL, NULL, 0, NULL},
217         {N_("/_Delete folder"),         NULL, folderview_rm_imap_folder_cb, 0, NULL},
218         {N_("/---"),                    NULL, NULL, 0, "<Separator>"},
219         {N_("/Remove _IMAP4 server"),   NULL, folderview_rm_imap_server_cb, 0, NULL},
220         {N_("/---"),                    NULL, NULL, 0, "<Separator>"},
221         {N_("/_Property..."),           NULL, NULL, 0, NULL}
222 };
223
224 static GtkItemFactoryEntry folderview_news_popup_entries[] =
225 {
226         {N_("/_Subscribe to newsgroup..."),
227                                          NULL, folderview_new_news_group_cb, 0, NULL},
228         {N_("/_Remove newsgroup"),       NULL, folderview_rm_news_group_cb, 0, NULL},
229         {N_("/---"),                     NULL, NULL, 0, "<Separator>"},
230         {N_("/Remove _news server"),     NULL, folderview_rm_news_server_cb, 0, NULL},
231         {N_("/---"),                     NULL, NULL, 0, "<Separator>"},
232         {N_("/_Property..."),            NULL, NULL, 0, NULL}
233 };
234
235
236 FolderView *folderview_create(void)
237 {
238         FolderView *folderview;
239         GtkWidget *scrolledwin;
240         GtkWidget *ctree;
241         gchar *titles[N_FOLDER_COLS] = {_("Folder"), _("New"),
242                                         _("Unread"), _("#")};
243         GtkWidget *mail_popup;
244         GtkWidget *news_popup;
245         GtkWidget *imap_popup;
246         GtkItemFactory *mail_factory;
247         GtkItemFactory *news_factory;
248         GtkItemFactory *imap_factory;
249         gint n_entries;
250         gint i;
251
252         debug_print(_("Creating folder view...\n"));
253         folderview = g_new0(FolderView, 1);
254
255         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
256         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
257                                        GTK_POLICY_AUTOMATIC,
258                                        GTK_POLICY_ALWAYS);
259         gtk_widget_set_usize(scrolledwin,
260                              prefs_common.folderview_width,
261                              prefs_common.folderview_height);
262         //                   COL_FOLDER_WIDTH + COL_NUM_WIDTH - 3, -1);
263
264         ctree = gtk_ctree_new_with_titles(N_FOLDER_COLS, COL_FOLDER, titles);
265         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
266         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
267         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_NEW,
268                                            GTK_JUSTIFY_RIGHT);
269         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_UNREAD,
270                                            GTK_JUSTIFY_RIGHT);
271         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_TOTAL,
272                                            GTK_JUSTIFY_RIGHT);
273         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_FOLDER,
274                                    prefs_common.folder_col_folder);
275         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_NEW,
276                                    prefs_common.folder_col_new);
277         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_UNREAD,        
278                                    prefs_common.folder_col_unread);
279         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_TOTAL,
280                                    prefs_common.folder_col_total);
281         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
282         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
283                                      GTK_CTREE_EXPANDER_SQUARE);
284         gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
285         //gtk_clist_set_reorderable(GTK_CLIST(ctree), TRUE);
286         /* don't let title buttons take key focus */
287         for (i = 0; i < N_FOLDER_COLS; i++)
288                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
289                                        GTK_CAN_FOCUS);
290
291         /* popup menu */
292         n_entries = sizeof(folderview_mail_popup_entries) /
293                 sizeof(folderview_mail_popup_entries[0]);
294         mail_popup = menu_create_items(folderview_mail_popup_entries,
295                                        n_entries,
296                                        "<MailFolder>", &mail_factory,
297                                        folderview);
298         n_entries = sizeof(folderview_imap_popup_entries) /
299                 sizeof(folderview_imap_popup_entries[0]);
300         imap_popup = menu_create_items(folderview_imap_popup_entries,
301                                        n_entries,
302                                        "<IMAPFolder>", &imap_factory,
303                                        folderview);
304         n_entries = sizeof(folderview_news_popup_entries) /
305                 sizeof(folderview_news_popup_entries[0]);
306         news_popup = menu_create_items(folderview_news_popup_entries,
307                                        n_entries,
308                                        "<NewsFolder>", &news_factory,
309                                        folderview);
310
311         gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
312                            GTK_SIGNAL_FUNC(folderview_key_pressed),
313                            folderview);
314         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
315                            GTK_SIGNAL_FUNC(folderview_button_pressed),
316                            folderview);
317         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
318                            GTK_SIGNAL_FUNC(folderview_button_released),
319                            folderview);
320         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
321                            GTK_SIGNAL_FUNC(folderview_selected), folderview);
322
323
324         gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_expand",
325                                  GTK_SIGNAL_FUNC(folderview_tree_expanded),
326                                  folderview);
327         gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_collapse",
328                                  GTK_SIGNAL_FUNC(folderview_tree_collapsed),
329                                  folderview);
330
331     gtk_signal_connect(GTK_OBJECT(ctree), "resize_column",
332                            GTK_SIGNAL_FUNC(folderview_col_resized),
333                            folderview);
334
335         gtk_signal_connect(GTK_OBJECT(mail_popup), "selection_done",
336                            GTK_SIGNAL_FUNC(folderview_popup_close),
337                            folderview);
338         gtk_signal_connect(GTK_OBJECT(imap_popup), "selection_done",
339                            GTK_SIGNAL_FUNC(folderview_popup_close),
340                            folderview);
341         gtk_signal_connect(GTK_OBJECT(news_popup), "selection_done",
342                            GTK_SIGNAL_FUNC(folderview_popup_close),
343                            folderview);
344
345         /* drop callback */
346         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL &
347                           ~GTK_DEST_DEFAULT_HIGHLIGHT,
348                           summary_drag_types, 1,
349                           GDK_ACTION_MOVE);
350         gtk_signal_connect(GTK_OBJECT(ctree), "drag_motion",
351                            GTK_SIGNAL_FUNC(folderview_drag_motion_cb),
352                            folderview);
353         gtk_signal_connect(GTK_OBJECT(ctree), "drag_leave",
354                            GTK_SIGNAL_FUNC(folderview_drag_leave_cb),
355                            folderview);
356         gtk_signal_connect(GTK_OBJECT(ctree), "drag_data_received",
357                            GTK_SIGNAL_FUNC(folderview_drag_received_cb),
358                            folderview);
359
360         folderview->scrolledwin  = scrolledwin;
361         folderview->ctree        = ctree;
362         folderview->mail_popup   = mail_popup;
363         folderview->mail_factory = mail_factory;
364         folderview->imap_popup   = imap_popup;
365         folderview->imap_factory = imap_factory;
366         folderview->news_popup   = news_popup;
367         folderview->news_factory = news_factory;
368
369         gtk_widget_show_all(scrolledwin);
370
371         folderview_list = g_list_append(folderview_list, folderview);
372
373         return folderview;
374 }
375
376 void folderview_init(FolderView *folderview)
377 {
378         GtkWidget *ctree = folderview->ctree;
379
380         PIXMAP_CREATE(ctree, inboxxpm, inboxxpmmask, inbox_xpm);
381         PIXMAP_CREATE(ctree, outboxxpm, outboxxpmmask, outbox_xpm);
382         PIXMAP_CREATE(ctree, folderxpm, folderxpmmask, DIRECTORY_CLOSE_XPM);
383         PIXMAP_CREATE(ctree, folderopenxpm, folderopenxpmmask,
384                       DIRECTORY_OPEN_XPM);
385         PIXMAP_CREATE(ctree, trashxpm, trashxpmmask, trash_xpm);
386
387         if (!normalfont)
388                 normalfont = gdk_fontset_load(NORMAL_FONT);
389         if (!boldfont)
390                 boldfont = gdk_fontset_load(BOLD_FONT);
391 }
392
393 void folderview_set(FolderView *folderview)
394 {
395         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
396         MainWindow *mainwin = folderview->mainwin;
397
398         debug_print(_("Setting folder info...\n"));
399         STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
400
401         main_window_cursor_wait(mainwin);
402
403         folderview->selected = NULL;
404         folderview->opened = NULL;
405
406         gtk_clist_freeze(GTK_CLIST(ctree));
407         gtk_clist_clear(GTK_CLIST(ctree));
408         gtk_clist_thaw(GTK_CLIST(ctree));
409         gtk_clist_freeze(GTK_CLIST(ctree));
410
411         folderview_set_folders(folderview);
412
413         gtk_clist_thaw(GTK_CLIST(ctree));
414         main_window_cursor_normal(mainwin);
415         STATUSBAR_POP(mainwin);
416 }
417
418 void folderview_select(FolderView *folderview, FolderItem *item)
419 {
420         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
421         GtkCTreeNode *node;
422
423         if (!item) return;
424
425         node = gtk_ctree_find_by_row_data(ctree, NULL, item);
426         if (node) folderview_select_node(folderview, node);
427 }
428
429 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
430 {
431         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
432
433         g_return_if_fail(node != NULL);
434
435         folderview->open_folder = TRUE;
436         gtk_ctree_select(ctree, node);
437         gtkut_ctree_set_focus_row(ctree, node);
438         if (folderview->summaryview->messages > 0)
439                 gtk_widget_grab_focus(folderview->summaryview->ctree);
440         else
441                 gtk_widget_grab_focus(folderview->ctree);
442 }
443
444 void folderview_unselect(FolderView *folderview)
445 {
446         if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
447                 gtk_ctree_collapse
448                         (GTK_CTREE(folderview->ctree), folderview->opened);
449
450         folderview->selected = folderview->opened = NULL;
451 }
452
453 void folderview_select_next_unread(FolderView *folderview)
454 {
455         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
456         GtkCTreeNode *node;
457         FolderItem *item;
458
459         if (folderview->opened)
460                 node = GTK_CTREE_NODE_NEXT(folderview->opened);
461         else
462                 node = gtk_ctree_node_nth(ctree, 1);
463
464         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
465                 item = gtk_ctree_node_get_row_data(ctree, node);
466                 if (item && (item->unread > 0 || item->new > 0) &&
467                     item->stype != F_TRASH) {
468                         folderview_select_node(folderview, node);
469                         return;
470                 }
471         }
472         if (!folderview->opened) return;
473         /* search again from the first node */
474         for (node = gtk_ctree_node_nth(ctree, 1); node != NULL;
475              node = GTK_CTREE_NODE_NEXT(node)) {
476                 item = gtk_ctree_node_get_row_data(ctree, node);
477                 if (item && (item->unread > 0 || item->new > 0) &&
478                     item->stype != F_TRASH) {
479                         folderview_select_node(folderview, node);
480                         return;
481                 }
482         }
483 }
484
485 void folderview_update_msg_num(FolderView *folderview, GtkCTreeNode *row,
486                                gint new, gint unread, gint total)
487 {
488         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
489         static GtkCTreeNode *prev_row = NULL;
490         FolderItem *item;
491
492         if (!row) return;
493
494         item = gtk_ctree_node_get_row_data(ctree, row);
495         if (!item) return;
496         if (prev_row     == row    &&
497             item->new    == new    &&
498             item->unread == unread &&
499             item->total  == total)
500                 return;
501
502         prev_row = row;
503
504         item->new    = new;
505         item->unread = unread;
506         item->total  = total;
507
508         folderview_update_node(folderview, row);
509 }
510
511 static void folderview_set_folders(FolderView *folderview)
512 {
513         GList *list;
514
515         list = folder_get_list();
516
517         for (; list != NULL; list = list->next)
518                 folderview_append_folder(folderview, FOLDER(list->data));
519 }
520
521 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
522                                       gpointer data)
523 {
524         GList *list;
525
526         for (list = folderview_list; list != NULL; list = list->next) {
527                 FolderView *folderview = (FolderView *)list->data;
528                 MainWindow *mainwin = folderview->mainwin;
529                 gchar *str;
530
531                 if (item->path)
532                         str = g_strdup_printf(_("Scanning folder %s%c%s ..."),
533                                               LOCAL_FOLDER(folder)->rootpath,
534                                               G_DIR_SEPARATOR,
535                                               item->path);
536                 else
537                         str = g_strdup_printf(_("Scanning folder %s ..."),
538                                               LOCAL_FOLDER(folder)->rootpath);
539
540                 STATUSBAR_PUSH(mainwin, str);
541                 STATUSBAR_POP(mainwin);
542                 g_free(str);
543         }
544 }
545
546 static GtkWidget *label_window_create(const gchar *str)
547 {
548         GtkWidget *window;
549         GtkWidget *label;
550
551         window = gtk_window_new(GTK_WINDOW_DIALOG);
552         gtk_widget_set_usize(window, 380, 60);
553         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
554         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
555         gtk_window_set_title(GTK_WINDOW(window), str);
556         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
557         gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
558         manage_window_set_transient(GTK_WINDOW(window));
559
560         label = gtk_label_new(str);
561         gtk_container_add(GTK_CONTAINER(window), label);
562         gtk_widget_show(label);
563
564         gtk_widget_show_now(window);
565
566         return window;
567 }
568
569 void folderview_update_all(void)
570 {
571         GList *list;
572         GtkWidget *window;
573
574         window = label_window_create(_("Updating all folders..."));
575
576         list = folder_get_list();
577         for (; list != NULL; list = list->next) {
578                 Folder *folder = list->data;
579
580                 if (!folder->scan_tree) continue;
581                 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
582                 folder->scan_tree(folder);
583                 folder_set_ui_func(folder, NULL, NULL);
584         }
585
586         folder_write_list();
587
588         for (list = folderview_list; list != NULL; list = list->next) {
589                 FolderView *folderview = (FolderView *)list->data;
590
591                 folderview_set(folderview);
592         }
593
594         gtk_widget_destroy(window);
595 }
596
597 static gboolean folderview_search_new_recursive(GtkCTree *ctree,
598                                                 GtkCTreeNode *node)
599 {
600         FolderItem *item;
601
602         if (node) {
603                 item = gtk_ctree_node_get_row_data(ctree, node);
604                 if (item) {
605                         if (item->new > 0 ||
606                             (item->stype == F_QUEUE && item->total > 0))
607                                 return TRUE;
608                 }
609                 node = GTK_CTREE_ROW(node)->children;
610         } else
611                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
612
613         while (node) {
614                 if (folderview_search_new_recursive(ctree, node) == TRUE)
615                         return TRUE;
616                 node = GTK_CTREE_ROW(node)->sibling;
617         }
618
619         return FALSE;
620 }
621
622 static gboolean folderview_have_new_children(FolderView *folderview,
623                                              GtkCTreeNode *node)
624 {
625         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
626
627         if (!node)
628                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
629         if (!node)
630                 return FALSE;
631
632         node = GTK_CTREE_ROW(node)->children;
633
634         while (node) {
635                 if (folderview_search_new_recursive(ctree, node) == TRUE)
636                         return TRUE;
637                 node = GTK_CTREE_ROW(node)->sibling;
638         }
639
640         return FALSE;
641 }
642
643 static gboolean folderview_search_unread_recursive(GtkCTree *ctree,
644                                                    GtkCTreeNode *node)
645 {
646         FolderItem *item;
647
648         if (node) {
649                 item = gtk_ctree_node_get_row_data(ctree, node);
650                 if (item) {
651                         if (item->unread > 0 ||
652                             (item->stype == F_QUEUE && item->total > 0))
653                                 return TRUE;
654                 }
655                 node = GTK_CTREE_ROW(node)->children;
656         } else
657                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
658
659         while (node) {
660                 if (folderview_search_unread_recursive(ctree, node) == TRUE)
661                         return TRUE;
662                 node = GTK_CTREE_ROW(node)->sibling;
663         }
664
665         return FALSE;
666 }
667
668 static gboolean folderview_have_unread_children(FolderView *folderview,
669                                                 GtkCTreeNode *node)
670 {
671         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
672
673         if (!node)
674                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
675         if (!node)
676                 return FALSE;
677
678         node = GTK_CTREE_ROW(node)->children;
679
680         while (node) {
681                 if (folderview_search_unread_recursive(ctree, node) == TRUE)
682                         return TRUE;
683                 node = GTK_CTREE_ROW(node)->sibling;
684         }
685
686         return FALSE;
687 }
688
689 static GtkCTreeNode *folderview_find_collapsed_parent(FolderView *folderview,
690                                                       GtkCTreeNode *node)
691 {
692         if (!node) return NULL;
693
694         while ((node = GTK_CTREE_ROW(node)->parent) != NULL) {
695                 if (!GTK_CTREE_ROW(node)->expanded)
696                         return node;
697         }
698
699         return NULL;
700 }
701
702 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
703 {
704         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
705         GtkStyle *style, *prev_style, *ctree_style;
706         FolderItem *item;
707         GdkPixmap *xpm, *openxpm;
708         GdkBitmap *mask, *openmask;
709         gchar *name;
710         gchar *str;
711         gboolean add_unread_mark;
712         gboolean use_bold, use_color;
713
714         item = gtk_ctree_node_get_row_data(ctree, node);
715         g_return_if_fail(item != NULL);
716
717         switch (item->stype) {
718         case F_INBOX:
719                 xpm = openxpm = inboxxpm;
720                 mask = openmask = inboxxpmmask;
721                 name = g_strdup(_("Inbox"));
722                 break;
723         case F_OUTBOX:
724                 xpm = openxpm = outboxxpm;
725                 mask = openmask = outboxxpmmask;
726                 name = g_strdup(_("Outbox"));
727                 break;
728         case F_QUEUE:
729                 xpm = openxpm = outboxxpm;
730                 mask = openmask = outboxxpmmask;
731                 name = g_strdup(_("Queue"));
732                 break;
733         case F_TRASH:
734                 xpm = openxpm = trashxpm;
735                 mask = openmask = trashxpmmask;
736                 name = g_strdup(_("Trash"));
737                 break;
738         case F_DRAFT:
739                 xpm = folderxpm;
740                 mask = folderxpmmask;
741                 openxpm = folderopenxpm;
742                 openmask = folderopenxpmmask;
743                 name = g_strdup(_("Draft"));
744                 break;
745         default:
746                 xpm = folderxpm;
747                 mask = folderxpmmask;
748                 openxpm = folderopenxpm;
749                 openmask = folderopenxpmmask;
750                 if (!item->parent) {
751                         switch (item->folder->type) {
752                         case F_MH:
753                                 name = " (MH)"; break;
754                         case F_IMAP:
755                                 name = " (IMAP4)"; break;
756                         case F_NEWS:
757                                 name = " (News)"; break;
758                         default:
759                                 name = "";
760                         }
761                         name = g_strconcat(item->name, name, NULL);
762                 } else
763                         name = g_strdup(item->name);
764         }
765
766         if (!GTK_CTREE_ROW(node)->expanded &&
767             folderview_have_unread_children(folderview, node))
768                 add_unread_mark = TRUE;
769         else
770                 add_unread_mark = FALSE;
771
772         if (item->stype == F_QUEUE && item->total > 0 &&
773             prefs_common.display_folder_unread) {
774                 str = g_strdup_printf("%s (%d%s)", name, item->total,
775                                       add_unread_mark ? "+" : "");
776                 gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
777                                         xpm, mask, openxpm, openmask,
778                                         FALSE, GTK_CTREE_ROW(node)->expanded);
779                 g_free(str);
780         } else if ((item->unread > 0 || add_unread_mark) &&
781                  prefs_common.display_folder_unread) {
782
783                 if (item->unread > 0)
784                         str = g_strdup_printf("%s (%d%s)", name, item->unread,
785                                               add_unread_mark ? "+" : "");
786                 else
787                         str = g_strdup_printf("%s (+)", name);
788                 str = g_strdup_printf("%s (%d)", name, item->unread);
789                 gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
790                                         xpm, mask, openxpm, openmask,
791                                         FALSE, GTK_CTREE_ROW(node)->expanded);
792                 g_free(str);
793         } else
794                 gtk_ctree_set_node_info(ctree, node, name, FOLDER_SPACING,
795                                         xpm, mask, openxpm, openmask,
796                                         FALSE, GTK_CTREE_ROW(node)->expanded);
797         g_free(name);
798
799         if (!item->parent) {
800                 gtk_ctree_node_set_text(ctree, node, COL_NEW,    "-");
801                 gtk_ctree_node_set_text(ctree, node, COL_UNREAD, "-");
802                 gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  "-");
803         } else {
804                 gtk_ctree_node_set_text(ctree, node, COL_NEW,    itos(item->new));
805                 gtk_ctree_node_set_text(ctree, node, COL_UNREAD, itos(item->unread));
806                 gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  itos(item->total));
807         }
808
809         if (item->stype == F_TRASH) return;
810
811         ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
812         prev_style = gtk_ctree_node_get_row_style(ctree, node);
813         if (!prev_style)
814                 prev_style = ctree_style;
815         style = gtk_style_copy(prev_style);
816         if (!style) return;
817
818         if (item->stype == F_QUEUE) {
819                 /* highlight queue folder if there are any messages */
820                 use_bold = use_color = (item->total > 0);
821         } else {
822                 /* if unread messages exist, print with bold font */
823                 use_bold = (item->unread > 0) || add_unread_mark;
824                 /* if new messages exist, print with colored letter */
825                 use_color =
826                         (item->new > 0) ||
827                         (add_unread_mark &&
828                          folderview_have_new_children(folderview, node));
829         }
830
831         if (use_bold && boldfont)
832                 style->font = boldfont;
833         else
834                 style->font = ctree_style->font;
835
836         if (use_color) {
837                 style->fg[GTK_STATE_NORMAL]   = folderview->color_new;
838                 style->fg[GTK_STATE_SELECTED] = folderview->color_new;
839         } else {
840                 style->fg[GTK_STATE_NORMAL] =
841                         ctree_style->fg[GTK_STATE_NORMAL];
842                 style->fg[GTK_STATE_SELECTED] =
843                         ctree_style->fg[GTK_STATE_SELECTED];
844         }
845
846         gtk_ctree_node_set_row_style(ctree, node, style);
847
848         if (use_bold) {
849                 GtkCTreeNode *parent;
850
851                 parent = folderview_find_collapsed_parent(folderview, node);
852                 if (parent)
853                         folderview_update_node(folderview, parent);
854         }
855 }
856
857 void folderview_update_item(FolderItem *item, gboolean update_summary)
858 {
859         GList *list;
860         FolderView *folderview;
861         GtkCTree *ctree;
862         GtkCTreeNode *node;
863
864         g_return_if_fail(item != NULL);
865
866         for (list = folderview_list; list != NULL; list = list->next) {
867                 folderview = (FolderView *)list->data;
868                 ctree = GTK_CTREE(folderview->ctree);
869
870                 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
871                 if (node) {
872                         folderview_update_node(folderview, node);
873                         if (update_summary && folderview->opened == node)
874                                 summary_show(folderview->summaryview,
875                                              item, FALSE);
876                 }
877         }
878 }
879
880 static void folderview_update_item_foreach_func(gpointer key, gpointer val,
881                                                 gpointer data)
882 {
883         folderview_update_item((FolderItem *)key, FALSE);
884 }
885
886 void folderview_update_item_foreach(GHashTable *table)
887 {
888         g_hash_table_foreach(table, folderview_update_item_foreach_func, NULL);
889 }
890
891 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
892                                       GNode *gnode, GtkCTreeNode *cnode,
893                                       gpointer data)
894 {
895         FolderView *folderview = (FolderView *)data;
896         FolderItem *item = FOLDER_ITEM(gnode->data);
897
898         g_return_val_if_fail(item != NULL, FALSE);
899
900         gtk_ctree_node_set_row_data(ctree, cnode, item);
901         folderview_update_node(folderview, cnode);
902
903         return TRUE;
904 }
905
906 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
907                                    gpointer data)
908 {
909         if (GTK_CTREE_ROW(node)->children)
910                 gtk_ctree_expand(ctree, node);
911 }
912
913 #define SET_SPECIAL_FOLDER(ctree, item) \
914 { \
915         if (item) { \
916                 GtkCTreeNode *node, *sibling; \
917  \
918                 node = gtk_ctree_find_by_row_data(ctree, root, item); \
919                 if (!node) \
920                         g_warning("%s not found.\n", item->path); \
921                 else { \
922                         if (!prev) \
923                                 sibling = GTK_CTREE_ROW(root)->children; \
924                         else \
925                                 sibling = GTK_CTREE_ROW(prev)->sibling; \
926                         if (node != sibling) \
927                                 gtk_ctree_move(ctree, node, root, sibling); \
928                 } \
929  \
930                 prev = node; \
931         } \
932 }
933
934 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
935                                     Folder *folder)
936 {
937         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
938         GtkCTreeNode *prev = NULL;
939
940         gtk_ctree_sort_recursive(ctree, root);
941
942         if (GTK_CTREE_ROW(root)->parent) return;
943
944         SET_SPECIAL_FOLDER(ctree, folder->inbox);
945         SET_SPECIAL_FOLDER(ctree, folder->outbox);
946         SET_SPECIAL_FOLDER(ctree, folder->draft);
947         SET_SPECIAL_FOLDER(ctree, folder->queue);
948         SET_SPECIAL_FOLDER(ctree, folder->trash);
949 }
950
951 static void folderview_append_folder(FolderView *folderview, Folder *folder)
952 {
953         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
954         GtkCTreeNode *root;
955
956         g_return_if_fail(folder != NULL);
957
958         root = gtk_ctree_insert_gnode(ctree, NULL, NULL, folder->node,
959                                       folderview_gnode_func, folderview);
960         gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
961                                 folderview);
962         folderview_sort_folders(folderview, root, folder);
963 }
964
965 void folderview_new_folder(FolderView *folderview)
966 {
967         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
968         FolderItem *item;
969
970         if (!folderview->selected) return;
971
972         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
973         g_return_if_fail(item != NULL);
974         g_return_if_fail(item->folder != NULL);
975
976         switch (item->folder->type) {
977         case F_MH:
978         case F_MBOX:
979         case F_MAILDIR:
980                 folderview_new_folder_cb(folderview, 0, NULL);
981                 break;
982         case F_IMAP:
983                 folderview_new_imap_folder_cb(folderview, 0, NULL);
984                 break;
985         case F_NEWS:
986         default:
987                 break;
988         }
989 }
990
991 void folderview_rename_folder(FolderView *folderview)
992 {
993         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
994         FolderItem *item;
995
996         if (!folderview->selected) return;
997
998         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
999         g_return_if_fail(item != NULL);
1000         g_return_if_fail(item->folder != NULL);
1001         if (!item->path) return;
1002         if (item->stype != F_NORMAL) return;
1003
1004         switch (item->folder->type) {
1005         case F_MH:
1006         case F_MBOX:
1007         case F_MAILDIR:
1008                 folderview_rename_folder_cb(folderview, 0, NULL);
1009                 break;
1010         case F_IMAP:
1011         case F_NEWS:
1012         default:
1013                 break;
1014         }
1015 }
1016
1017 void folderview_delete_folder(FolderView *folderview)
1018 {
1019         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1020         FolderItem *item;
1021
1022         if (!folderview->selected) return;
1023
1024         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1025         g_return_if_fail(item != NULL);
1026         g_return_if_fail(item->folder != NULL);
1027         if (!item->path) return;
1028         if (item->stype != F_NORMAL) return;
1029
1030         switch (item->folder->type) {
1031         case F_MH:
1032         case F_MBOX:
1033         case F_MAILDIR:
1034                 folderview_delete_folder_cb(folderview, 0, NULL);
1035                 break;
1036         case F_IMAP:
1037                 folderview_rm_imap_folder_cb(folderview, 0, NULL);
1038         case F_NEWS:
1039         default:
1040                 break;
1041         }
1042 }
1043
1044
1045 /* callback functions */
1046
1047 static void folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1048                                       FolderView *folderview)
1049 {
1050         GtkCList *clist = GTK_CLIST(ctree);
1051         gint prev_row = -1, row = -1, column = -1;
1052         FolderItem *item;
1053         Folder *folder;
1054
1055         if (!event) return;
1056
1057         if (event->button == 1) {
1058                 folderview->open_folder = TRUE;
1059                 return;
1060         }
1061
1062         if (event->button == 2 || event->button == 3) {
1063                 /* right clicked */
1064                 if (clist->selection) {
1065                         GtkCTreeNode *node;
1066
1067                         node = GTK_CTREE_NODE(clist->selection->data);
1068                         if (node)
1069                                 prev_row = gtkut_ctree_get_nth_from_node
1070                                         (GTK_CTREE(ctree), node);
1071                 }
1072
1073                 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1074                                                   &row, &column))
1075                         return;
1076                 if (prev_row != row) {
1077                         gtk_clist_unselect_all(clist);
1078                         if (event->button == 2)
1079                                 folderview_select_node
1080                                         (folderview,
1081                                          gtk_ctree_node_nth(GTK_CTREE(ctree),
1082                                                             row));
1083                         else
1084                                 gtk_clist_select_row(clist, row, column);
1085                 }
1086         }
1087
1088         if (event->button != 3) return;
1089
1090         item = gtk_clist_get_row_data(clist, row);
1091         g_return_if_fail(item != NULL);
1092         g_return_if_fail(item->folder != NULL);
1093         folder = item->folder;
1094
1095         menu_set_insensitive_all(GTK_MENU_SHELL(folderview->mail_popup));
1096         menu_set_insensitive_all(GTK_MENU_SHELL(folderview->imap_popup));
1097         menu_set_insensitive_all(GTK_MENU_SHELL(folderview->news_popup));
1098
1099         if (folder->type == F_MH && item->parent == NULL) {
1100                 menu_set_sensitive(folderview->mail_factory,
1101                                    "/Create new folder...", TRUE);
1102                 menu_set_sensitive(folderview->mail_factory,
1103                                    "/Remove mailbox", TRUE);
1104         } else if (folder->type == F_MH && item->stype != F_NORMAL) {
1105                 menu_set_sensitive(folderview->mail_factory,
1106                                    "/Create new folder...", TRUE);
1107         } else if (folder->type == F_MH) {
1108                 menu_set_sensitive(folderview->mail_factory,
1109                                    "/Create new folder...", TRUE);
1110                 menu_set_sensitive(folderview->mail_factory,
1111                                    "/Rename folder...", TRUE);
1112                 menu_set_sensitive(folderview->mail_factory,
1113                                    "/Delete folder", TRUE);
1114         } else if (folder->type == F_IMAP && item->parent == NULL) {
1115                 menu_set_sensitive(folderview->imap_factory,
1116                                    "/Create new folder...", TRUE);
1117                 menu_set_sensitive(folderview->imap_factory,
1118                                    "/Remove IMAP4 server", TRUE);
1119         } else if (folder->type == F_IMAP && item->parent != NULL) {
1120                 menu_set_sensitive(folderview->imap_factory,
1121                                    "/Create new folder...", TRUE);
1122                 menu_set_sensitive(folderview->imap_factory,
1123                                    "/Delete folder", TRUE);
1124         } else if (folder->type == F_NEWS && item->parent == NULL) {
1125                 menu_set_sensitive(folderview->news_factory,
1126                                    "/Subscribe to newsgroup...", TRUE);
1127                 menu_set_sensitive(folderview->news_factory,
1128                                    "/Remove news server", TRUE);
1129         } else if (folder->type == F_NEWS && item->parent != NULL) {
1130                 menu_set_sensitive(folderview->news_factory,
1131                                    "/Subscribe to newsgroup...", TRUE);
1132                 menu_set_sensitive(folderview->news_factory,
1133                                    "/Remove newsgroup", TRUE);
1134         }
1135
1136         if (folder->type == F_MH)
1137                 gtk_menu_popup(GTK_MENU(folderview->mail_popup), NULL, NULL,
1138                                NULL, NULL, event->button, event->time);
1139         else if (folder->type == F_IMAP)
1140                 gtk_menu_popup(GTK_MENU(folderview->imap_popup), NULL, NULL,
1141                                NULL, NULL, event->button, event->time);
1142         else if (folder->type == F_NEWS)
1143                 gtk_menu_popup(GTK_MENU(folderview->news_popup), NULL, NULL,
1144                                NULL, NULL, event->button, event->time);
1145 }
1146
1147 static void folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1148                                        FolderView *folderview)
1149 {
1150         if (!event) return;
1151
1152         if (event->button == 1 && folderview->opened != NULL) {
1153                 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
1154                 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
1155                                           folderview->opened);
1156         }
1157 }
1158
1159 #define BREAK_ON_MODIFIER_KEY() \
1160         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1161
1162 static void folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1163                                    FolderView *folderview)
1164 {
1165         if (!event) return;
1166
1167         switch (event->keyval) {
1168         case GDK_Return:
1169         case GDK_space:
1170                 if (folderview->selected) {
1171                         folderview_select_node(folderview,
1172                                                folderview->selected);
1173                 }
1174                 break;
1175         case GDK_v:
1176         case GDK_V:
1177         case GDK_g:
1178         case GDK_G:
1179         case GDK_x:
1180         case GDK_X:
1181         case GDK_w:
1182         case GDK_D:
1183         case GDK_Q:
1184                 BREAK_ON_MODIFIER_KEY();
1185                 summary_pass_key_press_event(folderview->summaryview, event);
1186         default:
1187         }
1188 }
1189
1190 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
1191                                 gint column, FolderView *folderview)
1192 {
1193         static gboolean can_select = TRUE;      /* exclusive lock */
1194         FolderItem *item;
1195
1196         folderview->selected = row;
1197
1198         if (folderview->opened == row) {
1199                 folderview->open_folder = FALSE;
1200                 return;
1201         }
1202
1203         if (!can_select) {
1204                 gtk_ctree_select(ctree, folderview->opened);
1205                 gtkut_ctree_set_focus_row(ctree, folderview->opened);
1206                 return;
1207         }
1208
1209         if (!folderview->open_folder) return;
1210         folderview->open_folder = FALSE;
1211
1212         item = gtk_ctree_node_get_row_data(ctree, row);
1213         if (!item) return;
1214
1215         can_select = FALSE;
1216
1217         if (item->path)
1218                 debug_print(_("Folder %s is selected\n"), item->path);
1219
1220         if (!GTK_CTREE_ROW(row)->children)
1221                 gtk_ctree_expand(ctree, row);
1222         if (folderview->opened &&
1223             !GTK_CTREE_ROW(folderview->opened)->children)
1224                 gtk_ctree_collapse(ctree, folderview->opened);
1225
1226         folderview->opened = row;
1227
1228         summary_show(folderview->summaryview, item, FALSE);
1229
1230         can_select = TRUE;
1231 }
1232
1233 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
1234                                      FolderView *folderview)
1235 {
1236         folderview_update_node(folderview, node);
1237 }
1238
1239 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
1240                                       FolderView *folderview)
1241 {
1242         folderview_update_node(folderview, node);
1243 }
1244
1245 static void folderview_popup_close(GtkMenuShell *menu_shell,
1246                                    FolderView *folderview)
1247 {
1248         if (!folderview->opened) return;
1249
1250         gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
1251         gtkut_ctree_set_focus_row(GTK_CTREE(folderview->ctree),
1252                                   folderview->opened);
1253 }
1254
1255 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
1256                                    FolderView *folderview)
1257 {
1258         switch (column) {
1259         case COL_FOLDER:
1260                 prefs_common.folder_col_folder = width;
1261                 break;
1262         case COL_NEW:
1263                 prefs_common.folder_col_new = width;
1264                 break;
1265         case COL_UNREAD:
1266                 prefs_common.folder_col_unread = width;
1267                 break;
1268         case COL_TOTAL:
1269                 prefs_common.folder_col_total = width;
1270                 break;
1271         default:
1272         }
1273 }
1274
1275 static GtkCTreeNode *folderview_find_by_name(GtkCTree *ctree,
1276                                              GtkCTreeNode *node,
1277                                              const gchar *name)
1278 {
1279         FolderItem *item;
1280
1281         if (!node)
1282                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1283         if (!node)
1284                 return NULL;
1285
1286         node = GTK_CTREE_ROW(node)->children;
1287
1288         while (node) {
1289                 item = gtk_ctree_node_get_row_data(ctree, node);
1290                 if (!folderview_compare_name(item, name))
1291                         return node;
1292                 node = GTK_CTREE_ROW(node)->sibling;
1293         }
1294
1295         return NULL;
1296 }
1297
1298 static void folderview_new_folder_cb(FolderView *folderview, guint action,
1299                                      GtkWidget *widget)
1300 {
1301         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1302         gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
1303         FolderItem *item;
1304         FolderItem *new_item;
1305         gchar *new_folder;
1306         GtkCTreeNode *node;
1307
1308         if (!folderview->selected) return;
1309
1310         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1311         g_return_if_fail(item != NULL);
1312         g_return_if_fail(item->folder != NULL);
1313
1314         new_folder = input_dialog(_("New folder"),
1315                                   _("Input the name of new folder:"),
1316                                   _("NewFolder"));
1317         if (!new_folder) return;
1318
1319         if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
1320                 alertpanel_error(_("`%c' can't be included in folder name."),
1321                                  G_DIR_SEPARATOR);
1322                 g_free(new_folder);
1323                 return;
1324         }
1325
1326         /* find whether the directory already exists */
1327         if (folderview_find_by_name(ctree, folderview->selected, new_folder)) {
1328                 alertpanel_error(_("The folder `%s' already exists."),
1329                                  new_folder);
1330                 g_free(new_folder);
1331                 return;
1332         }
1333
1334         new_item = item->folder->create_folder(item->folder, item, new_folder);
1335         g_free(new_folder);
1336         if (!new_item) return;
1337
1338         gtk_clist_freeze(GTK_CLIST(ctree));
1339
1340         text[COL_FOLDER] = new_item->name;
1341         node = gtk_ctree_insert_node(ctree, folderview->selected, NULL, text,
1342                                      FOLDER_SPACING,
1343                                      folderxpm, folderxpmmask,
1344                                      folderopenxpm, folderopenxpmmask,
1345                                      FALSE, FALSE);
1346         gtk_ctree_expand(ctree, folderview->selected);
1347         gtk_ctree_node_set_row_data(ctree, node, new_item);
1348         folderview_sort_folders(folderview, folderview->selected, item->folder);
1349
1350         gtk_clist_thaw(GTK_CLIST(ctree));
1351
1352         folder_write_list();
1353 }
1354
1355 static void folderview_rename_folder_cb(FolderView *folderview, guint action,
1356                                         GtkWidget *widget)
1357 {
1358         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1359         FolderItem *item;
1360         gchar *new_folder;
1361         gchar *message;
1362
1363         if (!folderview->selected) return;
1364
1365         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1366         g_return_if_fail(item != NULL);
1367         g_return_if_fail(item->path != NULL);
1368         g_return_if_fail(item->folder != NULL);
1369
1370         message = g_strdup_printf(_("Input new name for `%s':"),
1371                                   g_basename(item->path));
1372         new_folder = input_dialog(_("Rename folder"), message,
1373                                   g_basename(item->path));
1374         g_free(message);
1375         if (!new_folder) return;
1376
1377         if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
1378                 alertpanel_error(_("`%c' can't be included in folder name."),
1379                                  G_DIR_SEPARATOR);
1380                 g_free(new_folder);
1381                 return;
1382         }
1383
1384         if (folderview_find_by_name
1385                 (ctree, GTK_CTREE_ROW(folderview->selected)->parent,
1386                  new_folder)) {
1387                 alertpanel_error(_("The folder `%s' already exists."),
1388                                  new_folder);
1389                 g_free(new_folder);
1390                 return;
1391         }
1392
1393         if (item->folder->rename_folder(item->folder, item, new_folder) < 0) {
1394                 g_free(new_folder);
1395                 return;
1396         }
1397         g_free(new_folder);
1398
1399         gtk_clist_freeze(GTK_CLIST(ctree));
1400
1401         folderview_update_node(folderview, folderview->selected);
1402         folderview_sort_folders(folderview,
1403                                 GTK_CTREE_ROW(folderview->selected)->parent,
1404                                 item->folder);
1405         if (folderview->opened == folderview->selected) {
1406                 if (!GTK_CTREE_ROW(folderview->opened)->children)
1407                         gtk_ctree_expand(ctree, folderview->opened);
1408                 summary_show(folderview->summaryview, item, FALSE);
1409         }
1410
1411         gtk_clist_thaw(GTK_CLIST(ctree));
1412
1413         folder_write_list();
1414 }
1415
1416 static void folderview_delete_folder_cb(FolderView *folderview, guint action,
1417                                         GtkWidget *widget)
1418 {
1419         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1420         FolderItem *item;
1421         gchar *message;
1422         AlertValue avalue;
1423
1424         if (!folderview->selected) return;
1425
1426         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1427         g_return_if_fail(item != NULL);
1428         g_return_if_fail(item->path != NULL);
1429         g_return_if_fail(item->folder != NULL);
1430
1431         message = g_strdup_printf
1432                 (_("All folder(s) and message(s) under `%s' will be deleted.\n"
1433                    "Do you really want to delete?"),
1434                  g_basename(item->path));
1435         avalue = alertpanel(_("Delete folder"), message,
1436                             _("Yes"), _("+No"), NULL);
1437         g_free(message);
1438         if (avalue != G_ALERTDEFAULT) return;
1439
1440         if (item->folder->remove_folder(item->folder, item) < 0) {
1441                 g_warning(_("can't remove folder `%s'\n"), item->path);
1442                 return;
1443         }
1444
1445         if (folderview->opened == folderview->selected ||
1446             gtk_ctree_is_ancestor(ctree,
1447                                   folderview->selected,
1448                                   folderview->opened)) {
1449                 summary_clear_all(folderview->summaryview);
1450                 folderview->opened = NULL;
1451         }
1452
1453         gtk_ctree_remove_node(ctree, folderview->selected);
1454         folder_write_list();
1455 }
1456
1457 static void folderview_remove_mailbox_cb(FolderView *folderview, guint action,
1458                                          GtkWidget *widget)
1459 {
1460         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1461         GtkCTreeNode *node;
1462         FolderItem *item;
1463         gchar *message;
1464         AlertValue avalue;
1465
1466         if (!folderview->selected) return;
1467         node = folderview->selected;
1468         item = gtk_ctree_node_get_row_data(ctree, node);
1469         g_return_if_fail(item != NULL);
1470         g_return_if_fail(item->folder != NULL);
1471         if (item->parent) return;
1472
1473         message = g_strdup_printf
1474                 (_("Really remove the mailbox `%s' ?\n"
1475                    "(The messages are NOT deleted from disk)"),
1476                  item->folder->name);
1477         avalue = alertpanel(_("Remove folder"), message,
1478                             _("Yes"), _("+No"), NULL);
1479         g_free(message);
1480         if (avalue != G_ALERTDEFAULT) return;
1481
1482         folder_destroy(item->folder);
1483         summary_clear_all(folderview->summaryview);
1484         folderview_unselect(folderview);
1485         gtk_ctree_remove_node(ctree, node);
1486         folder_write_list();
1487 }
1488
1489 static void folderview_new_imap_folder_cb(FolderView *folderview, guint action,
1490                                           GtkWidget *widget)
1491 {
1492         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1493         gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
1494         GtkCTreeNode *node;
1495         FolderItem *item;
1496         FolderItem *new_item;
1497         gchar *new_folder;
1498
1499         if (!folderview->selected) return;
1500
1501         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1502         g_return_if_fail(item != NULL);
1503         g_return_if_fail(item->folder != NULL);
1504         g_return_if_fail(item->folder->type == F_IMAP);
1505         g_return_if_fail(item->folder->account != NULL);
1506
1507         new_folder = input_dialog(_("New folder"),
1508                                   _("Input the name of new folder:"),
1509                                   _("NewFolder"));
1510         if (!new_folder) return;
1511
1512         if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
1513                 alertpanel_error(_("`%c' can't be included in folder name."),
1514                                  G_DIR_SEPARATOR);
1515                 g_free(new_folder);
1516                 return;
1517         }
1518
1519         /* find whether the directory already exists */
1520         if (folderview_find_by_name(ctree, folderview->selected, new_folder)) {
1521                 alertpanel_error(_("The folder `%s' already exists."),
1522                                  new_folder);
1523                 g_free(new_folder);
1524                 return;
1525         }
1526
1527         new_item = item->folder->create_folder(item->folder, item, new_folder);
1528         g_free(new_folder);
1529         if (!new_item) return;
1530
1531         gtk_clist_freeze(GTK_CLIST(ctree));
1532
1533         text[COL_FOLDER] = new_item->name;
1534         node = gtk_ctree_insert_node(ctree, folderview->selected, NULL, text,
1535                                      FOLDER_SPACING,
1536                                      folderxpm, folderxpmmask,
1537                                      folderopenxpm, folderopenxpmmask,
1538                                      FALSE, FALSE);
1539         gtk_ctree_expand(ctree, folderview->selected);
1540         gtk_ctree_node_set_row_data(ctree, node, new_item);
1541         folderview_sort_folders(folderview, folderview->selected, item->folder);
1542
1543         gtk_clist_thaw(GTK_CLIST(ctree));
1544
1545         folder_write_list();
1546 }
1547
1548 static void folderview_rm_imap_folder_cb(FolderView *folderview, guint action,
1549                                          GtkWidget *widget)
1550 {
1551         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1552         FolderItem *item;
1553         gchar *message;
1554         AlertValue avalue;
1555
1556         if (!folderview->selected) return;
1557
1558         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1559         g_return_if_fail(item != NULL);
1560         g_return_if_fail(item->folder != NULL);
1561         g_return_if_fail(item->folder->type == F_IMAP);
1562         g_return_if_fail(item->folder->account != NULL);
1563
1564         message = g_strdup_printf(_("Really delete folder `%s'?"),
1565                                   g_basename(item->path));
1566         avalue = alertpanel(_("Delete folder"), message,
1567                             _("Yes"), _("+No"), NULL);
1568         g_free(message);
1569         if (avalue != G_ALERTDEFAULT) return;
1570
1571         if (item->folder->remove_folder(item->folder, item) < 0) {
1572                 g_warning(_("can't remove folder `%s'\n"), item->path);
1573                 return;
1574         }
1575
1576         if (folderview->opened == folderview->selected ||
1577             gtk_ctree_is_ancestor(ctree,
1578                                   folderview->selected,
1579                                   folderview->opened)) {
1580                 summary_clear_all(folderview->summaryview);
1581                 folderview->opened = NULL;
1582         }
1583
1584         gtk_ctree_remove_node(ctree, folderview->selected);
1585         folder_write_list();
1586 }
1587
1588 static void folderview_rm_imap_server_cb(FolderView *folderview, guint action,
1589                                          GtkWidget *widget)
1590 {
1591         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1592         FolderItem *item;
1593         gchar *message;
1594         AlertValue avalue;
1595
1596         if (!folderview->selected) return;
1597
1598         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1599         g_return_if_fail(item != NULL);
1600         g_return_if_fail(item->folder != NULL);
1601         g_return_if_fail(item->folder->type == F_IMAP);
1602         g_return_if_fail(item->folder->account != NULL);
1603
1604         message = g_strdup_printf(_("Really delete IMAP4 server `%s'?"),
1605                                   item->folder->name);
1606         avalue = alertpanel(_("Delete IMAP4 server"), message,
1607                             _("Yes"), _("+No"), NULL);
1608         g_free(message);
1609
1610         if (avalue != G_ALERTDEFAULT) return;
1611
1612         if (folderview->opened == folderview->selected ||
1613             gtk_ctree_is_ancestor(ctree,
1614                                   folderview->selected,
1615                                   folderview->opened)) {
1616                 summary_clear_all(folderview->summaryview);
1617                 folderview->opened = NULL;
1618         }
1619
1620         account_destroy(item->folder->account);
1621         folder_destroy(item->folder);
1622         gtk_ctree_remove_node(ctree, folderview->selected);
1623         account_set_menu();
1624         main_window_reflect_prefs_all();
1625         folder_write_list();
1626 }
1627
1628 static void folderview_new_news_group_cb(FolderView *folderview, guint action,
1629                                          GtkWidget *widget)
1630 {
1631         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1632         gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
1633         GtkCTreeNode *servernode, *node;
1634         FolderItem *item;
1635         FolderItem *newitem;
1636         gchar *new_group;
1637         const gchar *server;
1638
1639         if (!folderview->selected) return;
1640
1641         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1642         g_return_if_fail(item != NULL);
1643         g_return_if_fail(item->folder != NULL);
1644         g_return_if_fail(item->folder->type == F_NEWS);
1645         g_return_if_fail(item->folder->account != NULL);
1646
1647         new_group = grouplist_dialog(item);
1648         if (!new_group) return;
1649
1650         if (GTK_CTREE_ROW(folderview->selected)->parent != NULL)
1651                 servernode = GTK_CTREE_ROW(folderview->selected)->parent;
1652         else
1653                 servernode = folderview->selected;
1654
1655         if (folderview_find_by_name(ctree, servernode, new_group)) {
1656                 alertpanel_error(_("The newsgroup `%s' already exists."),
1657                                  new_group);
1658                 g_free(new_group);
1659                 return;
1660         }
1661
1662         gtk_clist_freeze(GTK_CLIST(ctree));
1663
1664         text[COL_FOLDER] = new_group;
1665         node = gtk_ctree_insert_node(ctree, servernode, NULL, text,
1666                                      FOLDER_SPACING,
1667                                      folderxpm, folderxpmmask,
1668                                      folderopenxpm, folderopenxpmmask,
1669                                      FALSE, FALSE);
1670         gtk_ctree_expand(ctree, servernode);
1671
1672         item = gtk_ctree_node_get_row_data(ctree, servernode);
1673         server = item->folder->account->nntp_server;
1674
1675         newitem = folder_item_new(new_group, new_group);
1676         g_free(new_group);
1677         folder_item_append(item, newitem);
1678         gtk_ctree_node_set_row_data(ctree, node, newitem);
1679         gtk_ctree_sort_node(ctree, servernode);
1680
1681         gtk_clist_thaw(GTK_CLIST(ctree));
1682
1683         folder_write_list();
1684 }
1685
1686 static void folderview_rm_news_group_cb(FolderView *folderview, guint action,
1687                                         GtkWidget *widget)
1688 {
1689         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1690         FolderItem *item;
1691         gchar *message;
1692         AlertValue avalue;
1693
1694         if (!folderview->selected) return;
1695
1696         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1697         g_return_if_fail(item != NULL);
1698         g_return_if_fail(item->folder != NULL);
1699         g_return_if_fail(item->folder->type == F_NEWS);
1700         g_return_if_fail(item->folder->account != NULL);
1701
1702         message = g_strdup_printf(_("Really delete newsgroup `%s'?"),
1703                                   g_basename(item->path));
1704         avalue = alertpanel(_("Delete newsgroup"), message,
1705                             _("Yes"), _("+No"), NULL);
1706         g_free(message);
1707         if (avalue != G_ALERTDEFAULT) return;
1708
1709         if (folderview->opened == folderview->selected) {
1710                 summary_clear_all(folderview->summaryview);
1711                 folderview->opened = NULL;
1712         }
1713
1714         folder_item_remove(item);
1715         gtk_ctree_remove_node(ctree, folderview->selected);
1716         folder_write_list();
1717 }
1718
1719 static void folderview_rm_news_server_cb(FolderView *folderview, guint action,
1720                                          GtkWidget *widget)
1721 {
1722         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1723         FolderItem *item;
1724         gchar *message;
1725         AlertValue avalue;
1726
1727         if (!folderview->selected) return;
1728
1729         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1730         g_return_if_fail(item != NULL);
1731         g_return_if_fail(item->folder != NULL);
1732         g_return_if_fail(item->folder->type == F_NEWS);
1733         g_return_if_fail(item->folder->account != NULL);
1734
1735         message = g_strdup_printf(_("Really delete news server `%s'?"),
1736                                   item->folder->name);
1737         avalue = alertpanel(_("Delete news server"), message,
1738                             _("Yes"), _("+No"), NULL);
1739         g_free(message);
1740
1741         if (avalue != G_ALERTDEFAULT) return;
1742
1743         if (folderview->opened == folderview->selected ||
1744             gtk_ctree_is_ancestor(ctree,
1745                                   folderview->selected,
1746                                   folderview->opened)) {
1747                 summary_clear_all(folderview->summaryview);
1748                 folderview->opened = NULL;
1749         }
1750
1751         account_destroy(item->folder->account);
1752         folder_destroy(item->folder);
1753         gtk_ctree_remove_node(ctree, folderview->selected);
1754         account_set_menu();
1755         main_window_reflect_prefs_all();
1756         folder_write_list();
1757 }
1758
1759 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
1760                                           GdkDragContext *context,
1761                                           gint            x,
1762                                           gint            y,
1763                                           guint           time,
1764                                           FolderView     *folderview)
1765 {
1766         gint row, column;
1767         FolderItem *item;
1768         GtkCTreeNode *node;
1769
1770         if (gtk_clist_get_selection_info(GTK_CLIST(widget),
1771                                          x - 24, y - 24, &row, &column)) {
1772                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
1773                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
1774                 if (item != NULL &&
1775                     item->path != NULL &&
1776                     folderview->summaryview->folder_item != item) {
1777                         if (item->folder->type != F_NEWS) {
1778                                 gtk_ctree_select(GTK_CTREE(widget), node);
1779                                 gdk_drag_status(context,
1780                                                 context->suggested_action,
1781                                                 time);
1782                                 return TRUE;
1783                         }
1784                 }
1785         }
1786
1787         gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
1788         gdk_drag_status(context, 0, time);
1789
1790         return FALSE;
1791 }
1792
1793 static void folderview_drag_leave_cb(GtkWidget      *widget,
1794                                      GdkDragContext *context,
1795                                      guint           time,
1796                                      FolderView     *folderview)
1797 {
1798         gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
1799 }
1800
1801 static void folderview_drag_received_cb(GtkWidget        *widget,
1802                                         GdkDragContext   *drag_context,
1803                                         gint              x,
1804                                         gint              y,
1805                                         GtkSelectionData *data,
1806                                         guint             info,
1807                                         guint             time,
1808                                         FolderView       *folderview)
1809 {
1810         gint row, column;
1811         FolderItem *item;
1812         GtkCTreeNode *node;
1813
1814         if (gtk_clist_get_selection_info(GTK_CLIST(widget),
1815                                          x - 24, y - 24, &row, &column) == 0)
1816                 return;
1817
1818         node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
1819         item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
1820         if (item != NULL) {
1821                 summary_move_selected_to(folderview->summaryview, item);
1822                 gtk_drag_finish(drag_context, TRUE, TRUE, time);
1823         } else
1824                 gtk_drag_finish(drag_context, FALSE, FALSE, time);
1825 }
1826
1827 static gint folderview_compare_name(gconstpointer a, gconstpointer b)
1828 {
1829         const FolderItem *item = a;
1830         const gchar *name = b;
1831
1832         if (!item->path) return -1;
1833         return strcmp2(g_basename(item->path), name);
1834 }