0.9.10claws50
[claws.git] / src / folderview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 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 "summary_search.h"
46 #include "inputdialog.h"
47 #include "manage_window.h"
48 #include "alertpanel.h"
49 #include "menu.h"
50 #include "stock_pixmap.h"
51 #include "procmsg.h"
52 #include "utils.h"
53 #include "gtkutils.h"
54 #include "prefs_common.h"
55 #include "prefs_account.h"
56 #include "prefs_filtering.h"
57 #include "prefs_folder_item.h"
58 #include "account.h"
59 #include "folder.h"
60 #include "foldersel.h"
61 #include "inc.h"
62 #include "statusbar.h"
63 #include "hooks.h"
64 #include "folderutils.h"
65
66 typedef enum
67 {
68         COL_FOLDER      = 0,
69         COL_NEW         = 1,
70         COL_UNREAD      = 2,
71         COL_TOTAL       = 3
72 } FolderColumnPos;
73
74 #define N_FOLDER_COLS           4
75 #define COL_FOLDER_WIDTH        150
76 #define COL_NUM_WIDTH           32
77
78 static GList *folderview_list = NULL;
79
80 static GtkStyle *normal_style;
81 static GtkStyle *normal_color_style;
82 static GtkStyle *bold_style;
83 static GtkStyle *bold_color_style;
84 static GtkStyle *bold_tgtfold_style;
85
86 static GdkBitmap *inboxxpm;
87 static GdkBitmap *inboxxpmmask;
88 static GdkPixmap *inboxhrmxpm;
89 static GdkBitmap *inboxhrmxpmmask;
90 static GdkPixmap *inboxopenxpm;
91 static GdkBitmap *inboxopenxpmmask;
92 static GdkPixmap *inboxopenhrmxpm;
93 static GdkBitmap *inboxopenhrmxpmmask;
94 static GdkPixmap *outboxxpm;
95 static GdkBitmap *outboxxpmmask;
96 static GdkPixmap *outboxhrmxpm;
97 static GdkBitmap *outboxhrmxpmmask;
98 static GdkPixmap *outboxopenxpm;
99 static GdkBitmap *outboxopenxpmmask;
100 static GdkPixmap *outboxopenhrmxpm;
101 static GdkBitmap *outboxopenhrmxpmmask;
102 static GdkPixmap *folderxpm;
103 static GdkBitmap *folderxpmmask;
104 static GdkPixmap *folderhrmxpm;
105 static GdkBitmap *folderhrmxpmmask;
106 static GdkPixmap *folderopenxpm;
107 static GdkBitmap *folderopenxpmmask;
108 static GdkPixmap *folderopenhrmxpm;
109 static GdkBitmap *folderopenhrmxpmmask;
110 static GdkPixmap *trashopenxpm;
111 static GdkBitmap *trashopenxpmmask;
112 static GdkPixmap *trashopenhrmxpm;
113 static GdkBitmap *trashopenhrmxpmmask;
114 static GdkPixmap *trashxpm;
115 static GdkBitmap *trashxpmmask;
116 static GdkPixmap *trashhrmxpm;
117 static GdkBitmap *trashhrmxpmmask;
118 static GdkPixmap *queuexpm;
119 static GdkBitmap *queuexpmmask;
120 static GdkPixmap *queuehrmxpm;
121 static GdkBitmap *queuehrmxpmmask;
122 static GdkPixmap *queueopenxpm;
123 static GdkBitmap *queueopenxpmmask;
124 static GdkPixmap *queueopenhrmxpm;
125 static GdkBitmap *queueopenhrmxpmmask;
126 static GdkPixmap *newxpm;
127 static GdkBitmap *newxpmmask;
128 static GdkPixmap *unreadxpm;
129 static GdkBitmap *unreadxpmmask;
130 static GdkPixmap *draftsxpm;
131 static GdkBitmap *draftsxpmmask;
132 static GdkPixmap *draftsopenxpm;
133 static GdkBitmap *draftsopenxpmmask;
134
135 static void folderview_select_node       (FolderView    *folderview,
136                                           GtkCTreeNode  *node);
137 static void folderview_set_folders       (FolderView    *folderview);
138 static void folderview_sort_folders      (FolderView    *folderview,
139                                           GtkCTreeNode  *root,
140                                           Folder        *folder);
141 static void folderview_append_folder     (FolderView    *folderview,
142                                           Folder        *folder);
143 static void folderview_update_node       (FolderView    *folderview,
144                                           GtkCTreeNode  *node);
145
146 static gint folderview_clist_compare    (GtkCList       *clist,
147                                          gconstpointer   ptr1,
148                                          gconstpointer   ptr2);
149
150 /* callback functions */
151 static void folderview_button_pressed   (GtkWidget      *ctree,
152                                          GdkEventButton *event,
153                                          FolderView     *folderview);
154 static void folderview_button_released  (GtkWidget      *ctree,
155                                          GdkEventButton *event,
156                                          FolderView     *folderview);
157 static void folderview_key_pressed      (GtkWidget      *widget,
158                                          GdkEventKey    *event,
159                                          FolderView     *folderview);
160 static void folderview_selected         (GtkCTree       *ctree,
161                                          GtkCTreeNode   *row,
162                                          gint            column,
163                                          FolderView     *folderview);
164 static void folderview_tree_expanded    (GtkCTree       *ctree,
165                                          GtkCTreeNode   *node,
166                                          FolderView     *folderview);
167 static void folderview_tree_collapsed   (GtkCTree       *ctree,
168                                          GtkCTreeNode   *node,
169                                          FolderView     *folderview);
170 static void folderview_popup_close      (GtkMenuShell   *menu_shell,
171                                          FolderView     *folderview);
172 static void folderview_col_resized      (GtkCList       *clist,
173                                          gint            column,
174                                          gint            width,
175                                          FolderView     *folderview);
176
177 static void mark_all_read_cb            (FolderView    *folderview,
178                                          guint           action,
179                                          GtkWidget      *widget);
180
181 static void folderview_search_cb        (FolderView     *folderview,
182                                          guint           action,
183                                          GtkWidget      *widget);
184
185 static void folderview_property_cb      (FolderView     *folderview,
186                                          guint           action,
187                                          GtkWidget      *widget);
188
189 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
190                                           GdkDragContext *context,
191                                           gint            x,
192                                           gint            y,
193                                           guint           time,
194                                           FolderView     *folderview);
195 static void folderview_drag_leave_cb     (GtkWidget        *widget,
196                                           GdkDragContext   *context,
197                                           guint             time,
198                                           FolderView       *folderview);
199 static void folderview_drag_received_cb  (GtkWidget        *widget,
200                                           GdkDragContext   *drag_context,
201                                           gint              x,
202                                           gint              y,
203                                           GtkSelectionData *data,
204                                           guint             info,
205                                           guint             time,
206                                           FolderView       *folderview);
207 static void folderview_start_drag        (GtkWidget *widget, gint button, GdkEvent *event,
208                                           FolderView       *folderview);
209 static void folderview_drag_data_get     (GtkWidget        *widget,
210                                           GdkDragContext   *drag_context,
211                                           GtkSelectionData *selection_data,
212                                           guint             info,
213                                           guint             time,
214                                           FolderView       *folderview);
215 static void folderview_drag_end_cb       (GtkWidget        *widget,
216                                           GdkDragContext   *drag_context,
217                                           FolderView       *folderview);
218
219 void folderview_create_folder_node       (FolderView       *folderview, 
220                                           FolderItem       *item);
221 gboolean folderview_update_folder        (gpointer          source,
222                                           gpointer          userdata);
223 gboolean folderview_update_item_claws    (gpointer          source,
224                                           gpointer          data);
225 static void folderview_processing_cb(FolderView *folderview, guint action,
226                                      GtkWidget *widget);
227
228 GHashTable *folderview_popups;
229
230 static GtkItemFactoryEntry folderview_common_popup_entries[] =
231 {
232         {N_("/Mark all _read"),         NULL, mark_all_read_cb, 0, NULL},
233         {N_("/_Search folder..."),      NULL, folderview_search_cb, 0, NULL},
234         {N_("/_Properties..."),         NULL, folderview_property_cb, 0, NULL},
235         {N_("/_Processing..."),         NULL, folderview_processing_cb, 0, NULL},
236 };
237
238 GtkTargetEntry folderview_drag_types[] =
239 {
240         {"text/plain", GTK_TARGET_SAME_APP, TARGET_DUMMY}
241 };
242
243 void folderview_initialize(void)
244 {
245         FolderViewPopup *fpopup;
246         guint i, n_entries;
247         GSList *entries = NULL;
248
249         fpopup = g_new0(FolderViewPopup, 1);
250
251         n_entries = sizeof(folderview_common_popup_entries) /
252                 sizeof(folderview_common_popup_entries[0]);
253         for (i = 0; i < n_entries; i++)
254                 entries = g_slist_append(entries, &folderview_common_popup_entries[i]);
255
256         fpopup->klass = "common";
257         fpopup->path = "<CommonFolder>";
258         fpopup->entries = entries;
259         fpopup->set_sensitivity = NULL;
260
261         folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
262         g_hash_table_insert(folderview_popups, "common", fpopup);
263 }
264
265 static GtkItemFactory *create_ifactory(FolderView *folderview, FolderViewPopup *fpopup)
266 {
267         GSList *entries;
268         GtkItemFactory *factory;
269         FolderViewPopup *fpopup_common;
270         GtkWidget *popup;
271
272         factory = gtk_item_factory_new(GTK_TYPE_MENU, fpopup->path, NULL);
273         gtk_item_factory_set_translate_func(factory, menu_translate,
274                                             NULL, NULL);
275
276         for (entries = fpopup->entries; entries != NULL; entries = g_slist_next(entries))
277                 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
278
279         fpopup_common = g_hash_table_lookup(folderview_popups, "common");
280         if (fpopup_common != fpopup)
281                 for (entries = fpopup_common->entries; entries != NULL; entries = g_slist_next(entries))
282                         gtk_item_factory_create_item(factory, entries->data, folderview, 1);
283
284         popup = gtk_item_factory_get_widget(factory, fpopup->path);
285         gtk_signal_connect(GTK_OBJECT(popup), "selection_done",
286                            GTK_SIGNAL_FUNC(folderview_popup_close),
287                            folderview);
288
289         return factory;
290 }
291
292 static void create_ifactories(gpointer key, gpointer value, gpointer data)
293 {
294         FolderView *folderview = data;
295         FolderViewPopup *fpopup = value;
296         GtkItemFactory *factory;
297
298         factory = create_ifactory(folderview, fpopup);
299         g_hash_table_insert(folderview->popups, fpopup->klass, factory);
300 }
301
302 FolderView *folderview_create(void)
303 {
304         FolderView *folderview;
305         GtkWidget *scrolledwin;
306         GtkWidget *ctree;
307         gchar *titles[N_FOLDER_COLS];
308         gint i;
309
310         debug_print("Creating folder view...\n");
311         folderview = g_new0(FolderView, 1);
312
313         titles[COL_FOLDER] = _("Folder");
314         titles[COL_NEW]    = _("New");
315         titles[COL_UNREAD] = _("Unread");
316         titles[COL_TOTAL]  = _("#");
317
318         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
319         gtk_scrolled_window_set_policy
320                 (GTK_SCROLLED_WINDOW(scrolledwin),
321                  GTK_POLICY_AUTOMATIC,
322                  prefs_common.folderview_vscrollbar_policy);
323         gtk_widget_set_usize(scrolledwin,
324                              prefs_common.folderview_width,
325                              prefs_common.folderview_height);
326
327         ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, COL_FOLDER, titles);
328         
329         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
330         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
331 #ifndef CLAWS /* text instead of pixmaps */
332         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_NEW,
333                                            GTK_JUSTIFY_RIGHT);
334         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_UNREAD,
335                                            GTK_JUSTIFY_RIGHT);
336 #endif                                     
337         gtk_clist_set_column_justification(GTK_CLIST(ctree), COL_TOTAL,
338                                            GTK_JUSTIFY_RIGHT);
339         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_FOLDER,
340                                    prefs_common.folder_col_folder);
341         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_NEW,
342                                    prefs_common.folder_col_new);
343         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_UNREAD,        
344                                    prefs_common.folder_col_unread);
345         gtk_clist_set_column_width(GTK_CLIST(ctree), COL_TOTAL,
346                                    prefs_common.folder_col_total);
347         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
348         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
349                                      GTK_CTREE_EXPANDER_SQUARE);
350         gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
351         gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
352
353         /* create popup factories */
354         folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
355         g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
356
357         /* don't let title buttons take key focus */
358         for (i = 0; i < N_FOLDER_COLS; i++)
359                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
360                                        GTK_CAN_FOCUS);
361
362         gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
363                            GTK_SIGNAL_FUNC(folderview_key_pressed),
364                            folderview);
365         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
366                            GTK_SIGNAL_FUNC(folderview_button_pressed),
367                            folderview);
368         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
369                            GTK_SIGNAL_FUNC(folderview_button_released),
370                            folderview);
371         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
372                            GTK_SIGNAL_FUNC(folderview_selected), folderview);
373         gtk_signal_connect(GTK_OBJECT(ctree), "start_drag",
374                            GTK_SIGNAL_FUNC(folderview_start_drag), folderview);
375         gtk_signal_connect(GTK_OBJECT(ctree), "drag_data_get",
376                            GTK_SIGNAL_FUNC(folderview_drag_data_get),
377                            folderview);
378
379         gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_expand",
380                                  GTK_SIGNAL_FUNC(folderview_tree_expanded),
381                                  folderview);
382         gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_collapse",
383                                  GTK_SIGNAL_FUNC(folderview_tree_collapsed),
384                                  folderview);
385
386         gtk_signal_connect(GTK_OBJECT(ctree), "resize_column",
387                            GTK_SIGNAL_FUNC(folderview_col_resized),
388                            folderview);
389
390         /* drop callback */
391         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
392                           summary_drag_types, 1,
393                           GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
394         gtk_signal_connect(GTK_OBJECT(ctree), "drag_motion",
395                            GTK_SIGNAL_FUNC(folderview_drag_motion_cb),
396                            folderview);
397         gtk_signal_connect(GTK_OBJECT(ctree), "drag_leave",
398                            GTK_SIGNAL_FUNC(folderview_drag_leave_cb),
399                            folderview);
400         gtk_signal_connect(GTK_OBJECT(ctree), "drag_data_received",
401                            GTK_SIGNAL_FUNC(folderview_drag_received_cb),
402                            folderview);
403         gtk_signal_connect(GTK_OBJECT(ctree), "drag_end",
404                            GTK_SIGNAL_FUNC(folderview_drag_end_cb),
405                            folderview);
406
407         folderview->scrolledwin  = scrolledwin;
408         folderview->ctree        = ctree;
409
410         folderview->folder_update_callback_id =
411                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
412         folderview->folder_item_update_callback_id =
413                 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
414
415         gtk_widget_show_all(scrolledwin);
416
417         folderview->target_list = gtk_target_list_new(folderview_drag_types, 1);
418         folderview_list = g_list_append(folderview_list, folderview);
419
420         return folderview;
421 }
422
423 void folderview_init(FolderView *folderview)
424 {
425         static GdkFont *boldfont = NULL;
426         static GdkFont *normalfont = NULL;
427         GtkWidget *ctree = folderview->ctree;
428         GtkWidget *label_new;
429         GtkWidget *label_unread;
430         GtkWidget *hbox_new;
431         GtkWidget *hbox_unread;
432         
433         gtk_widget_realize(ctree);
434         stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
435         stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
436         stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
437         stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
438         stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
439         stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
440         stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
441         stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
442         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
443         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
444         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
445         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
446         stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
447         stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
448         stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
449         stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
450         stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
451         stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
452         stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
453         stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
454         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
455         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
456
457         /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
458          * instead text (text overflows making them unreadable and ugly) */
459         stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
460                          &newxpm, &newxpmmask);
461         stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
462                          &unreadxpm, &unreadxpmmask);
463                 
464         label_new = gtk_pixmap_new(newxpm, newxpmmask);
465         label_unread = gtk_pixmap_new(unreadxpm, unreadxpmmask);
466
467         hbox_new = gtk_hbox_new(FALSE, 4);
468         hbox_unread = gtk_hbox_new(FALSE, 4);
469
470         /* left justified */
471         gtk_box_pack_start(GTK_BOX(hbox_new),label_new,FALSE,FALSE,0);
472         gtk_box_pack_start(GTK_BOX(hbox_unread),label_unread,FALSE,FALSE,0);
473
474         gtk_widget_show_all(hbox_new);
475         gtk_widget_show_all(hbox_unread);
476
477         gtk_clist_set_column_widget(GTK_CLIST(ctree),COL_NEW,hbox_new);
478         gtk_clist_set_column_widget(GTK_CLIST(ctree),COL_UNREAD,hbox_unread);
479                         
480         if (!normal_style) {
481                 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
482                 if (!normalfont)
483                         normalfont = gtkut_font_load(prefs_common.normalfont);
484                 if (normalfont)
485                         normal_style->font = normalfont;
486                 normal_color_style = gtk_style_copy(normal_style);
487                 normal_color_style->fg[GTK_STATE_NORMAL] = folderview->color_new;
488
489                 gtk_widget_set_style(ctree, normal_style);
490         }
491
492         if (!bold_style) {
493                 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
494                 if (!boldfont)
495                         boldfont = gtkut_font_load(prefs_common.boldfont);
496                 if (boldfont)
497                         bold_style->font = boldfont;
498                 bold_color_style = gtk_style_copy(bold_style);
499                 bold_color_style->fg[GTK_STATE_NORMAL] = folderview->color_new;
500
501                 bold_tgtfold_style = gtk_style_copy(bold_style);
502                 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
503         }
504 }
505
506 void folderview_set(FolderView *folderview)
507 {
508         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
509         MainWindow *mainwin = folderview->mainwin;
510
511         debug_print("Setting folder info...\n");
512         STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
513
514         main_window_cursor_wait(mainwin);
515
516         folderview->selected = NULL;
517         folderview->opened = NULL;
518
519         gtk_clist_freeze(GTK_CLIST(ctree));
520         gtk_clist_clear(GTK_CLIST(ctree));
521         gtk_clist_thaw(GTK_CLIST(ctree));
522         gtk_clist_freeze(GTK_CLIST(ctree));
523
524         folderview_set_folders(folderview);
525
526         gtk_clist_thaw(GTK_CLIST(ctree));
527         main_window_cursor_normal(mainwin);
528         STATUSBAR_POP(mainwin);
529 }
530
531 void folderview_set_all(void)
532 {
533         GList *list;
534
535         for (list = folderview_list; list != NULL; list = list->next)
536                 folderview_set((FolderView *)list->data);
537 }
538
539 void folderview_select(FolderView *folderview, FolderItem *item)
540 {
541         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
542         GtkCTreeNode *node;
543         GtkCTreeNode *old_selected = folderview->selected;
544
545         if (!item) return;
546
547         node = gtk_ctree_find_by_row_data(ctree, NULL, item);
548         if (node) folderview_select_node(folderview, node);
549
550         if (old_selected != node)
551                 folder_update_op_count();
552 }
553
554 static void mark_all_read_cb(FolderView *folderview, guint action,
555                              GtkWidget *widget)
556 {
557         FolderItem *item;
558
559         item = folderview_get_selected(folderview);
560         if (item == NULL)
561                 return;
562
563         folderutils_mark_all_read(item);
564 }
565
566 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
567 {
568         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
569
570         g_return_if_fail(node != NULL);
571
572         folderview->open_folder = TRUE;
573         gtkut_ctree_set_focus_row(ctree, node);
574         gtk_ctree_select(ctree, node);
575         if (folderview->summaryview->folder_item &&
576             folderview->summaryview->folder_item->total_msgs > 0)
577                 gtk_widget_grab_focus(folderview->summaryview->ctree);
578         else
579                 gtk_widget_grab_focus(folderview->ctree);
580
581         gtkut_ctree_expand_parent_all(ctree, node);
582 }
583
584 void folderview_unselect(FolderView *folderview)
585 {
586         if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
587                 gtk_ctree_collapse
588                         (GTK_CTREE(folderview->ctree), folderview->opened);
589
590         folderview->selected = folderview->opened = NULL;
591 }
592
593 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
594                                                  GtkCTreeNode *node)
595 {
596         FolderItem *item;
597
598         if (node)
599                 node = gtkut_ctree_node_next(ctree, node);
600         else
601                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
602
603         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
604                 item = gtk_ctree_node_get_row_data(ctree, node);
605                 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
606                         return node;
607         }
608
609         return NULL;
610 }
611
612 void folderview_select_next_unread(FolderView *folderview)
613 {
614         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
615         GtkCTreeNode *node = NULL;
616
617         if ((node = folderview_find_next_unread(ctree, folderview->opened))
618             != NULL) {
619                 folderview_select_node(folderview, node);
620                 return;
621         }
622
623         if (!folderview->opened ||
624             folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list))
625                 return;
626         /* search again from the first node */
627         if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
628                 folderview_select_node(folderview, node);
629 }
630
631 void folderview_update_msg_num(FolderView *folderview, GtkCTreeNode *row)
632 {
633         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
634         static GtkCTreeNode *prev_row = NULL;
635         FolderItem *item;
636         gint new, unread, total;
637         gchar *new_str, *unread_str, *total_str;
638
639         if (!row) return;
640
641         item = gtk_ctree_node_get_row_data(ctree, row);
642         if (!item) return;
643
644         gtk_ctree_node_get_text(ctree, row, COL_NEW, &new_str);
645         gtk_ctree_node_get_text(ctree, row, COL_UNREAD, &unread_str);
646         gtk_ctree_node_get_text(ctree, row, COL_TOTAL, &total_str);
647         new = atoi(new_str);
648         unread = atoi(unread_str);
649         total = atoi(total_str);
650
651         prev_row = row;
652
653         folderview_update_node(folderview, row);
654 }
655
656 void folderview_append_item(FolderItem *item)
657 {
658         GList *list;
659
660         g_return_if_fail(item != NULL);
661         g_return_if_fail(item->folder != NULL);
662         if (folder_item_parent(item)) return;
663
664         for (list = folderview_list; list != NULL; list = list->next) {
665                 FolderView *folderview = (FolderView *)list->data;
666                 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
667                 GtkCTreeNode *node, *child;
668
669                 node = gtk_ctree_find_by_row_data(ctree, NULL, 
670                                                   folder_item_parent(item));
671                 if (node) {
672                         child = gtk_ctree_find_by_row_data(ctree, node, item);
673                         if (!child) {
674                                 gchar *text[N_FOLDER_COLS] =
675                                         {NULL, "0", "0", "0"};
676
677                                 gtk_clist_freeze(GTK_CLIST(ctree));
678
679                                 text[COL_FOLDER] = item->name;
680                                 child = gtk_ctree_insert_node
681                                         (ctree, node, NULL, text,
682                                          FOLDER_SPACING,
683                                          folderxpm, folderxpmmask,
684                                          folderopenxpm, folderopenxpmmask,
685                                          FALSE, FALSE);
686                                 gtk_ctree_node_set_row_data(ctree, child, item);
687                                 gtk_ctree_expand(ctree, node);
688                                 folderview_update_node(folderview, child);
689                                 folderview_sort_folders(folderview, node,
690                                                         item->folder);
691
692                                 gtk_clist_thaw(GTK_CLIST(ctree));
693                         }
694                 }
695         }
696 }
697
698 static void folderview_set_folders(FolderView *folderview)
699 {
700         GList *list;
701
702         list = folder_get_list();
703
704         for (; list != NULL; list = list->next)
705                 folderview_append_folder(folderview, FOLDER(list->data));
706 }
707
708 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
709                                       gpointer data)
710 {
711         GList *list;
712
713         for (list = folderview_list; list != NULL; list = list->next) {
714                 FolderView *folderview = (FolderView *)list->data;
715                 MainWindow *mainwin = folderview->mainwin;
716                 gchar *str;
717
718                 if (item->path)
719                         str = g_strdup_printf(_("Scanning folder %s%c%s ..."),
720                                               item->folder->name, G_DIR_SEPARATOR,
721                                               item->path);
722                 else
723                         str = g_strdup_printf(_("Scanning folder %s ..."),
724                                               item->folder->name);
725
726                 STATUSBAR_PUSH(mainwin, str);
727                 STATUSBAR_POP(mainwin);
728                 g_free(str);
729         }
730 }
731
732 static GtkWidget *label_window_create(const gchar *str)
733 {
734         GtkWidget *window;
735         GtkWidget *label;
736
737         window = gtk_window_new(GTK_WINDOW_DIALOG);
738         gtk_widget_set_usize(window, 380, 60);
739         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
740         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
741         gtk_window_set_title(GTK_WINDOW(window), str);
742         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
743         gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
744         manage_window_set_transient(GTK_WINDOW(window));
745
746         label = gtk_label_new(str);
747         gtk_container_add(GTK_CONTAINER(window), label);
748         gtk_widget_show(label);
749
750         gtk_widget_show_now(window);
751
752         return window;
753 }
754
755 void folderview_rescan_tree(Folder *folder)
756 {
757         GtkWidget *window;
758
759         g_return_if_fail(folder != NULL);
760
761         if (!folder->klass->scan_tree) return;
762
763         inc_lock();
764         window = label_window_create(_("Rebuilding folder tree..."));
765
766         folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
767         folder_scan_tree(folder);
768         folder_set_ui_func(folder, NULL, NULL);
769
770         folderview_set_all();
771
772         gtk_widget_destroy(window);
773         inc_unlock();
774 }
775
776 /** folderview_check_new()
777  *  Scan and update the folder and return the 
778  *  count the number of new messages since last check. 
779  *  \param folder the folder to check for new messages
780  *  \return the number of new messages since last check
781  */
782 gint folderview_check_new(Folder *folder)
783 {
784         GList *list;
785         FolderItem *item;
786         FolderView *folderview;
787         GtkCTree *ctree;
788         GtkCTreeNode *node;
789         gint new_msgs = 0;
790         gint former_new_msgs = 0;
791         gint former_new = 0;
792
793         for (list = folderview_list; list != NULL; list = list->next) {
794                 folderview = (FolderView *)list->data;
795                 ctree = GTK_CTREE(folderview->ctree);
796
797                 inc_lock();
798                 main_window_lock(folderview->mainwin);
799                 gtk_widget_set_sensitive(folderview->ctree, FALSE);
800
801                 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
802                      node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
803                         item = gtk_ctree_node_get_row_data(ctree, node);
804                         if (!item || !item->path || !item->folder) continue;
805                         if (item->no_select) continue;
806                         if (folder && folder != item->folder) continue;
807                         if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
808                         if (!item->prefs->newmailcheck) continue;
809
810                         folderview_scan_tree_func(item->folder, item, NULL);
811                         former_new = item->new_msgs;
812                         if (folder_item_scan(item) < 0) {
813                                 if (folder && !FOLDER_IS_LOCAL(folder))
814                                         break;
815                         }
816                         folderview_update_node(folderview, node);
817                         new_msgs += item->new_msgs;
818                         former_new_msgs += former_new;
819                 }
820
821                 gtk_widget_set_sensitive(folderview->ctree, TRUE);
822                 main_window_unlock(folderview->mainwin);
823                 inc_unlock();
824         }
825
826         folder_write_list();
827         /* Number of new messages since last check is the just the difference 
828          * between former_new_msgs and new_msgs. If new_msgs is less than
829          * former_new_msgs, that would mean another session accessed the folder
830          * and the result is not well defined.
831          */
832         new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
833         return new_msgs;
834 }
835
836 void folderview_check_new_all(void)
837 {
838         GList *list;
839         GtkWidget *window;
840         FolderView *folderview;
841
842         folderview = (FolderView *)folderview_list->data;
843
844         inc_lock();
845         main_window_lock(folderview->mainwin);
846         window = label_window_create
847                 (_("Checking for new messages in all folders..."));
848
849         list = folder_get_list();
850         for (; list != NULL; list = list->next) {
851                 Folder *folder = list->data;
852
853                 folderview_check_new(folder);
854         }
855
856         folder_write_list();
857         folderview_set_all();
858
859         gtk_widget_destroy(window);
860         main_window_unlock(folderview->mainwin);
861         inc_unlock();
862 }
863
864 static gboolean folderview_search_new_recursive(GtkCTree *ctree,
865                                                 GtkCTreeNode *node)
866 {
867         FolderItem *item;
868
869         if (node) {
870                 item = gtk_ctree_node_get_row_data(ctree, node);
871                 if (item) {
872                         if (item->new_msgs > 0 ||
873                             (item->stype == F_QUEUE && item->total_msgs > 0))
874                                 return TRUE;
875                 }
876                 node = GTK_CTREE_ROW(node)->children;
877         } else
878                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
879
880         while (node) {
881                 if (folderview_search_new_recursive(ctree, node) == TRUE)
882                         return TRUE;
883                 node = GTK_CTREE_ROW(node)->sibling;
884         }
885
886         return FALSE;
887 }
888
889 static gboolean folderview_have_new_children(FolderView *folderview,
890                                              GtkCTreeNode *node)
891 {
892         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
893
894         if (!node)
895                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
896         if (!node)
897                 return FALSE;
898
899         node = GTK_CTREE_ROW(node)->children;
900
901         while (node) {
902                 if (folderview_search_new_recursive(ctree, node) == TRUE)
903                         return TRUE;
904                 node = GTK_CTREE_ROW(node)->sibling;
905         }
906
907         return FALSE;
908 }
909
910 static gboolean folderview_search_unread_recursive(GtkCTree *ctree,
911                                                    GtkCTreeNode *node)
912 {
913         FolderItem *item;
914
915         if (node) {
916                 item = gtk_ctree_node_get_row_data(ctree, node);
917                 if (item) {
918                         if (item->unread_msgs > 0 ||
919                             (item->stype == F_QUEUE && item->total_msgs > 0))
920                                 return TRUE;
921                 }
922                 node = GTK_CTREE_ROW(node)->children;
923         } else
924                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
925
926         while (node) {
927                 if (folderview_search_unread_recursive(ctree, node) == TRUE)
928                         return TRUE;
929                 node = GTK_CTREE_ROW(node)->sibling;
930         }
931
932         return FALSE;
933 }
934
935 static gboolean folderview_have_unread_children(FolderView *folderview,
936                                                 GtkCTreeNode *node)
937 {
938         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
939
940         if (!node)
941                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
942         if (!node)
943                 return FALSE;
944
945         node = GTK_CTREE_ROW(node)->children;
946
947         while (node) {
948                 if (folderview_search_unread_recursive(ctree, node) == TRUE)
949                         return TRUE;
950                 node = GTK_CTREE_ROW(node)->sibling;
951         }
952
953         return FALSE;
954 }
955
956 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
957 {
958         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
959         GtkStyle *style = NULL;
960         GtkStyle *color_style = NULL;
961         FolderItem *item;
962         GdkPixmap *xpm, *openxpm;
963         GdkBitmap *mask, *openmask;
964         gchar *name;
965         gchar *str;
966         gboolean add_unread_mark;
967         gboolean use_bold, use_color;
968
969         item = gtk_ctree_node_get_row_data(ctree, node);
970         g_return_if_fail(item != NULL);
971
972         switch (item->stype) {
973         case F_INBOX:
974                 if (item->hide_read_msgs) {
975                         xpm = inboxhrmxpm;
976                         mask = inboxhrmxpmmask;
977                         openxpm = inboxopenhrmxpm;
978                         openmask = inboxopenhrmxpmmask;
979                 } else {
980                         xpm = inboxxpm;
981                         mask = inboxxpmmask;
982                         openxpm = inboxopenxpm;
983                         openmask = inboxopenxpmmask;
984                 }
985                 break;
986         case F_OUTBOX:
987                 if (item->hide_read_msgs) {
988                         xpm = outboxhrmxpm;
989                         mask = outboxhrmxpmmask;
990                         openxpm = outboxopenhrmxpm;
991                         openmask = outboxopenhrmxpmmask;
992                 } else {
993                         xpm = outboxxpm;
994                         mask = outboxxpmmask;
995                         openxpm = outboxopenxpm;
996                         openmask = outboxopenxpmmask;
997                 }
998                 break;
999         case F_QUEUE:
1000                 if (item->hide_read_msgs) {
1001                         xpm = queuehrmxpm;
1002                         mask = queuehrmxpmmask;
1003                         openxpm = queueopenhrmxpm;
1004                         openmask = queueopenhrmxpmmask;
1005                 } else {
1006                         xpm = queuexpm;
1007                         mask = queuexpmmask;
1008                         openxpm = queueopenxpm;
1009                         openmask = queueopenxpmmask;
1010                 }
1011                 break;
1012         case F_TRASH:
1013                 if (item->hide_read_msgs) {
1014                         xpm = trashhrmxpm;
1015                         mask = trashhrmxpmmask;
1016                         openxpm = trashopenhrmxpm;
1017                         openmask = trashopenhrmxpmmask;
1018                 } else {
1019                         xpm = trashxpm;
1020                         mask = trashxpmmask;
1021                         openxpm = trashopenxpm;
1022                         openmask = trashopenxpmmask;
1023                 }
1024                 break;
1025         case F_DRAFT:
1026                 xpm = draftsxpm;
1027                 mask = draftsxpmmask;
1028                 openxpm = draftsopenxpm;
1029                 openmask = draftsopenxpmmask;
1030                 break;
1031         default:
1032                 if (item->hide_read_msgs) {
1033                         xpm = folderhrmxpm;
1034                         mask = folderhrmxpmmask;
1035                         openxpm = folderopenhrmxpm;
1036                         openmask = folderopenhrmxpmmask;
1037                 } else {
1038                         xpm = folderxpm;
1039                         mask = folderxpmmask;
1040                         openxpm = folderopenxpm;
1041                         openmask = folderopenxpmmask;
1042                 }
1043         }
1044         name = folder_item_get_name(item);
1045
1046         if (!GTK_CTREE_ROW(node)->expanded &&
1047             folderview_have_unread_children(folderview, node))
1048                 add_unread_mark = TRUE;
1049         else
1050                 add_unread_mark = FALSE;
1051
1052         if (item->stype == F_QUEUE && item->total_msgs > 0 &&
1053             prefs_common.display_folder_unread) {
1054                 str = g_strdup_printf("%s (%d%s)", name, item->total_msgs,
1055                                       add_unread_mark ? "+" : "");
1056                 gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1057                                         xpm, mask, openxpm, openmask,
1058                                         FALSE, GTK_CTREE_ROW(node)->expanded);
1059                 g_free(str);
1060         } else if ((item->unread_msgs > 0 || add_unread_mark) &&
1061                  prefs_common.display_folder_unread) {
1062
1063                 if (item->unread_msgs > 0)
1064                         str = g_strdup_printf("%s (%d%s%s)", name, item->unread_msgs,
1065                                               add_unread_mark ? "+" : "", 
1066                                               item->unreadmarked_msgs > 0 ? "!":"");
1067                 else
1068                         str = g_strdup_printf("%s (+)", name);
1069                 gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1070                                         xpm, mask, openxpm, openmask,
1071                                         FALSE, GTK_CTREE_ROW(node)->expanded);
1072                 g_free(str);
1073         } else {
1074                 str = g_strdup_printf("%s%s", name, 
1075                                       item->unreadmarked_msgs > 0 ? " (!)":"");
1076         
1077                 gtk_ctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1078                                         xpm, mask, openxpm, openmask,
1079                                         FALSE, GTK_CTREE_ROW(node)->expanded);
1080                 g_free(str);
1081         }
1082         g_free(name);
1083
1084         if (!folder_item_parent(item)) {
1085                 gtk_ctree_node_set_text(ctree, node, COL_NEW,    "-");
1086                 gtk_ctree_node_set_text(ctree, node, COL_UNREAD, "-");
1087                 gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  "-");
1088         } else {
1089                 gtk_ctree_node_set_text(ctree, node, COL_NEW,    itos(item->new_msgs));
1090                 gtk_ctree_node_set_text(ctree, node, COL_UNREAD, itos(item->unread_msgs));
1091                 gtk_ctree_node_set_text(ctree, node, COL_TOTAL,  itos(item->total_msgs));
1092         }
1093
1094         if (item->stype == F_OUTBOX || item->stype == F_DRAFT ||
1095             item->stype == F_TRASH) {
1096                 use_bold = use_color = FALSE;
1097         } else if (item->stype == F_QUEUE) {
1098                 /* highlight queue folder if there are any messages */
1099                 use_bold = use_color = (item->total_msgs > 0);
1100         } else {
1101                 /* if unread messages exist, print with bold font */
1102                 use_bold = (item->unread_msgs > 0) || add_unread_mark;
1103                 /* if new messages exist, print with colored letter */
1104                 use_color =
1105                         (item->new_msgs > 0) ||
1106                         (add_unread_mark &&
1107                          folderview_have_new_children(folderview, node));       
1108         }
1109
1110         gtk_ctree_node_set_foreground(ctree, node, NULL);
1111
1112         if (use_bold) {
1113                 if (item->prefs->color > 0 && !use_color) {
1114                         GdkColor gdk_color;
1115
1116                         gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1117                         color_style = gtk_style_copy(bold_style);
1118                         color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1119                         style = color_style;
1120                 } else if (use_color)
1121                         style = bold_color_style;
1122                 else
1123                         style = bold_style;
1124                 if (item->op_count > 0) {
1125                         style = bold_tgtfold_style;
1126                 }
1127         } else if (use_color) {
1128                 style = normal_color_style;
1129                 gtk_ctree_node_set_foreground(ctree, node,
1130                                               &folderview->color_new);
1131         } else if (item->op_count > 0) {
1132                 style = bold_tgtfold_style;
1133         } else if (item->prefs->color > 0) {
1134                 GdkColor gdk_color;
1135
1136                 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1137                 color_style = gtk_style_copy(normal_style);
1138                 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1139                 style = color_style;
1140         } else {
1141                 style = normal_style;
1142         }
1143
1144         gtk_ctree_node_set_row_style(ctree, node, style);
1145
1146         if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1147                 folderview_update_node(folderview, node);
1148 }
1149
1150 #if !CLAWS /* keep it here for syncs */
1151 void folderview_update_item(FolderItem *item, gboolean update_summary)
1152 {
1153         GList *list;
1154         FolderView *folderview;
1155         GtkCTree *ctree;
1156         GtkCTreeNode *node;
1157
1158         g_return_if_fail(item != NULL);
1159
1160         for (list = folderview_list; list != NULL; list = list->next) {
1161                 folderview = (FolderView *)list->data;
1162                 ctree = GTK_CTREE(folderview->ctree);
1163
1164                 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1165                 if (node) {
1166                         folderview_update_node(folderview, node);
1167                         if (update_summary && folderview->opened == node)
1168                                 summary_show(folderview->summaryview,
1169                                              item, FALSE);
1170                 }
1171         }
1172 }
1173 #endif
1174
1175 gboolean folderview_update_item_claws(gpointer source, gpointer data)
1176 {
1177         FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1178         FolderView *folderview = (FolderView *)data;
1179         GtkCTree *ctree;
1180         GtkCTreeNode *node;
1181
1182         g_return_val_if_fail(update_info != NULL, TRUE);
1183         g_return_val_if_fail(update_info->item != NULL, TRUE);
1184         g_return_val_if_fail(folderview != NULL, FALSE);
1185
1186         ctree = GTK_CTREE(folderview->ctree);
1187
1188         node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1189         if (node) {
1190                 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1191                         folderview_update_node(folderview, node);
1192                 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) && (folderview->opened == node))
1193                         summary_show(folderview->summaryview, update_info->item);
1194         }
1195         
1196         return FALSE;
1197 }
1198
1199 static void folderview_update_item_foreach_func(gpointer key, gpointer val,
1200                                                 gpointer data)
1201 {
1202         /* CLAWS: share this joy with other hook functions ... */
1203         folder_item_update((FolderItem *)key, 
1204                            (FolderItemUpdateFlags)GPOINTER_TO_INT(data));
1205 }
1206
1207 void folderview_update_item_foreach(GHashTable *table, gboolean update_summary)
1208 {
1209         GList *list;
1210         FolderItemUpdateFlags flags;
1211         
1212         flags = update_summary ?  F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_MSGCNT
1213                 : 0;
1214         for (list = folderview_list; list != NULL; list = list->next)
1215                 g_hash_table_foreach(table, folderview_update_item_foreach_func, 
1216                                      GINT_TO_POINTER(flags));
1217 }
1218
1219 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1220                                       GNode *gnode, GtkCTreeNode *cnode,
1221                                       gpointer data)
1222 {
1223         FolderView *folderview = (FolderView *)data;
1224         FolderItem *item = FOLDER_ITEM(gnode->data);
1225
1226         g_return_val_if_fail(item != NULL, FALSE);
1227
1228         gtk_ctree_node_set_row_data(ctree, cnode, item);
1229         folderview_update_node(folderview, cnode);
1230
1231         return TRUE;
1232 }
1233
1234 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1235                                    gpointer data)
1236 {
1237         FolderView *folderview = (FolderView *)data;
1238         FolderItem *item;
1239
1240         if (GTK_CTREE_ROW(node)->children) {
1241                 item = gtk_ctree_node_get_row_data(ctree, node);
1242                 g_return_if_fail(item != NULL);
1243
1244                 if (!item->collapsed)
1245                         gtk_ctree_expand(ctree, node);
1246                 else
1247                         folderview_update_node(folderview, node);
1248         }
1249 }
1250
1251 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1252                                GtkCTreeNode *root, GtkCTreeNode **prev)
1253 {
1254         if (item) {
1255                 GtkCTreeNode *node, *parent, *sibling;
1256
1257                 node = gtk_ctree_find_by_row_data(ctree, root, item);
1258                 if (!node)
1259                         g_warning("%s not found.\n", item->path);
1260                 else {
1261                         parent = GTK_CTREE_ROW(node)->parent;
1262                         if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1263                                 sibling = GTK_CTREE_ROW(*prev)->sibling;
1264                         else
1265                                 sibling = GTK_CTREE_ROW(parent)->children;
1266                         while (sibling) {
1267                                 FolderItem *tmp;
1268
1269                                 tmp = gtk_ctree_node_get_row_data
1270                                         (ctree, sibling);
1271                                 if (tmp->stype != F_NORMAL)
1272                                         sibling = GTK_CTREE_ROW(sibling)->sibling;
1273                                 else
1274                                         break;
1275                         }
1276                         if (node != sibling)
1277                                 gtk_ctree_move(ctree, node, parent, sibling);
1278                 }
1279
1280                 *prev = node;
1281         }
1282 }
1283
1284 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1285                                     Folder *folder)
1286 {
1287         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1288         GtkCTreeNode *prev = NULL;
1289
1290         gtk_sctree_sort_recursive(ctree, root);
1291
1292         if (root && GTK_CTREE_ROW(root)->parent) return;
1293
1294         set_special_folder(ctree, folder->inbox, root, &prev);
1295         set_special_folder(ctree, folder->outbox, root, &prev);
1296         set_special_folder(ctree, folder->draft, root, &prev);
1297         set_special_folder(ctree, folder->queue, root, &prev);
1298         set_special_folder(ctree, folder->trash, root, &prev);
1299 }
1300
1301 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1302 {
1303         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1304         GtkCTreeNode *root;
1305
1306         g_return_if_fail(folder != NULL);
1307
1308         root = gtk_ctree_insert_gnode(ctree, NULL, NULL, folder->node,
1309                                       folderview_gnode_func, folderview);
1310         gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1311                                 folderview);
1312         folderview_sort_folders(folderview, root, folder);
1313 }
1314
1315 /* callback functions */
1316
1317 static void folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1318                                       FolderView *folderview)
1319 {
1320         GtkCList *clist = GTK_CLIST(ctree);
1321         gint prev_row = -1, row = -1, column = -1;
1322         FolderItem *item;
1323         Folder *folder;
1324         FolderViewPopup *fpopup;
1325         GtkItemFactory *fpopup_factory;
1326         GtkWidget *popup;
1327
1328         if (!event) return;
1329
1330         if (event->button == 1) {
1331                 folderview->open_folder = TRUE;
1332                 return;
1333         }
1334
1335         if (event->button == 2 || event->button == 3) {
1336                 /* right clicked */
1337                 if (clist->selection) {
1338                         GtkCTreeNode *node;
1339
1340                         node = GTK_CTREE_NODE(clist->selection->data);
1341                         if (node)
1342                                 prev_row = gtkut_ctree_get_nth_from_node
1343                                         (GTK_CTREE(ctree), node);
1344                 }
1345
1346                 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1347                                                   &row, &column))
1348                         return;
1349                 if (prev_row != row) {
1350                         gtk_clist_unselect_all(clist);
1351                         if (event->button == 2)
1352                                 folderview_select_node
1353                                         (folderview,
1354                                          gtk_ctree_node_nth(GTK_CTREE(ctree),
1355                                                             row));
1356                         else
1357                                 gtk_clist_select_row(clist, row, column);
1358                 }
1359         }
1360
1361         if (event->button != 3) return;
1362
1363         item = gtk_clist_get_row_data(clist, row);
1364         g_return_if_fail(item != NULL);
1365         g_return_if_fail(item->folder != NULL);
1366         folder = item->folder;
1367
1368         fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1369         if (fpopup != NULL)
1370                 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1371         else {
1372                 fpopup = g_hash_table_lookup(folderview_popups, "common");
1373                 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1374         }
1375
1376         if (fpopup->set_sensitivity != NULL)
1377                 fpopup->set_sensitivity(fpopup_factory, item);
1378
1379 #define SET_SENS(name, sens) \
1380         menu_set_sensitive(fpopup_factory, name, sens)
1381
1382         SET_SENS("/Mark all read", item->unread_msgs >= 1);
1383         SET_SENS("/Search folder...", item->total_msgs >= 1);
1384         SET_SENS("/Properties...", TRUE);
1385         SET_SENS("/Processing...", item->node->parent != NULL);
1386
1387 #undef SET_SENS
1388
1389         popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1390         gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1391                        event->button, event->time);
1392 }
1393
1394 static void folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1395                                        FolderView *folderview)
1396 {
1397         if (!event) return;
1398
1399         if (event->button == 1 && folderview->open_folder == FALSE &&
1400             folderview->opened != NULL) {
1401                 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
1402                                           folderview->opened);
1403                 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
1404         }
1405 }
1406
1407 static void folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1408                                    FolderView *folderview)
1409 {
1410         if (!event) return;
1411
1412         switch (event->keyval) {
1413         case GDK_Return:
1414                 if (folderview->selected) {
1415                         folderview_select_node(folderview,
1416                                                folderview->selected);
1417                 }
1418                 break;
1419         case GDK_space:
1420                 if (folderview->selected) {
1421                         if (folderview->opened == folderview->selected &&
1422                             (!folderview->summaryview->folder_item ||
1423                              folderview->summaryview->folder_item->total_msgs == 0))
1424                                 folderview_select_next_unread(folderview);
1425                         else
1426                                 folderview_select_node(folderview,
1427                                                        folderview->selected);
1428                 }
1429                 break;
1430         default:
1431                 break;
1432         }
1433 }
1434
1435 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
1436                                 gint column, FolderView *folderview)
1437 {
1438         static gboolean can_select = TRUE;      /* exclusive lock */
1439         gboolean opened;
1440         FolderItem *item;
1441         gchar *buf;
1442
1443         folderview->selected = row;
1444
1445         if (folderview->opened == row) {
1446                 folderview->open_folder = FALSE;
1447                 return;
1448         }
1449
1450         if (!can_select || summary_is_locked(folderview->summaryview)) {
1451                 gtkut_ctree_set_focus_row(ctree, folderview->opened);
1452                 gtk_ctree_select(ctree, folderview->opened);
1453                 return;
1454         }
1455
1456         if (!folderview->open_folder) return;
1457
1458         item = gtk_ctree_node_get_row_data(ctree, row);
1459         if (!item) return;
1460
1461         can_select = FALSE;
1462
1463         /* Save cache for old folder */
1464         /* We don't want to lose all caches if sylpheed crashed */
1465         if (folderview->opened) {
1466                 FolderItem *olditem;
1467                 
1468                 olditem = gtk_ctree_node_get_row_data(ctree, folderview->opened);
1469                 if (olditem) {
1470                         /* will be null if we just moved the previously opened folder */
1471                         summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
1472                         folder_item_close(olditem);
1473                 }
1474         }
1475
1476         /* CLAWS: set compose button type: news folder items 
1477          * always have a news folder as parent */
1478         if (item->folder) 
1479                 toolbar_set_compose_button
1480                         (folderview->mainwin->toolbar,
1481                          FOLDER_TYPE(item->folder) == F_NEWS ? 
1482                          COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
1483
1484         if (item->path)
1485                 debug_print("Folder %s is selected\n", item->path);
1486
1487         if (!GTK_CTREE_ROW(row)->children)
1488                 gtk_ctree_expand(ctree, row);
1489         if (folderview->opened &&
1490             !GTK_CTREE_ROW(folderview->opened)->children)
1491                 gtk_ctree_collapse(ctree, folderview->opened);
1492
1493         /* ungrab the mouse event */
1494         if (GTK_WIDGET_HAS_GRAB(ctree)) {
1495                 gtk_grab_remove(GTK_WIDGET(ctree));
1496                 if (gdk_pointer_is_grabbed())
1497                         gdk_pointer_ungrab(GDK_CURRENT_TIME);
1498         }
1499
1500         /* Open Folder */
1501         buf = g_strdup_printf(_("Opening Folder %s..."), item->path ? 
1502                                         item->path : "(null)");
1503         debug_print("%s\n", buf);
1504         STATUSBAR_PUSH(folderview->mainwin, buf);
1505         g_free(buf);
1506
1507         main_window_cursor_wait(folderview->mainwin);
1508
1509         if (folder_item_open(item) != 0) {
1510                 main_window_cursor_normal(folderview->mainwin);
1511                 STATUSBAR_POP(folderview->mainwin);
1512
1513                 alertpanel_error(_("Folder could not be opened."));
1514
1515                 return;
1516         }
1517
1518         main_window_cursor_normal(folderview->mainwin);
1519
1520         /* Show messages */
1521         summary_set_prefs_from_folderitem(folderview->summaryview, item);
1522         opened = summary_show(folderview->summaryview, item);
1523         
1524         folder_clean_cache_memory();
1525
1526         if (!opened) {
1527                 gtkut_ctree_set_focus_row(ctree, folderview->opened);
1528                 gtk_ctree_select(ctree, folderview->opened);
1529         } else {
1530                 folderview->opened = row;
1531                 if (gtk_ctree_node_is_visible(ctree, row)
1532                     != GTK_VISIBILITY_FULL)
1533                         gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
1534         }
1535
1536         STATUSBAR_POP(folderview->mainwin);
1537
1538         folderview->open_folder = FALSE;
1539         can_select = TRUE;
1540 }
1541
1542 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
1543                                      FolderView *folderview)
1544 {
1545         FolderItem *item;
1546
1547         item = gtk_ctree_node_get_row_data(ctree, node);
1548         g_return_if_fail(item != NULL);
1549         item->collapsed = FALSE;
1550         folderview_update_node(folderview, node);
1551 }
1552
1553 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
1554                                       FolderView *folderview)
1555 {
1556         FolderItem *item;
1557
1558         item = gtk_ctree_node_get_row_data(ctree, node);
1559         g_return_if_fail(item != NULL);
1560         item->collapsed = TRUE;
1561         folderview_update_node(folderview, node);
1562 }
1563
1564 static void folderview_popup_close(GtkMenuShell *menu_shell,
1565                                    FolderView *folderview)
1566 {
1567         if (!folderview->opened) return;
1568
1569         gtkut_ctree_set_focus_row(GTK_CTREE(folderview->ctree),
1570                                   folderview->opened);
1571         gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
1572 }
1573
1574 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
1575                                    FolderView *folderview)
1576 {
1577         switch (column) {
1578         case COL_FOLDER:
1579                 prefs_common.folder_col_folder = width;
1580                 break;
1581         case COL_NEW:
1582                 prefs_common.folder_col_new = width;
1583                 break;
1584         case COL_UNREAD:
1585                 prefs_common.folder_col_unread = width;
1586                 break;
1587         case COL_TOTAL:
1588                 prefs_common.folder_col_total = width;
1589                 break;
1590         default:
1591                 break;
1592         }
1593 }
1594
1595 void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
1596 {
1597         GNode *srcnode;
1598
1599         folderview_create_folder_node(folderview, item);
1600         
1601         srcnode = item->folder->node;   
1602         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1603         srcnode = srcnode->children;
1604         while (srcnode != NULL) {
1605                 if (srcnode && srcnode->data) {
1606                         FolderItem *next_item = (FolderItem*) srcnode->data;
1607                         folderview_create_folder_node_recursive(folderview, next_item);
1608                 }
1609                 srcnode = srcnode->next;
1610         }
1611 }
1612
1613 void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
1614 {
1615         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1616         gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
1617         GtkCTreeNode *node, *parent_node;
1618         
1619         parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
1620         if (parent_node == NULL)
1621                 return;
1622
1623         gtk_clist_freeze(GTK_CLIST(ctree));
1624
1625         text[COL_FOLDER] = item->name;
1626         node = gtk_ctree_insert_node(ctree, parent_node, NULL, text,
1627                                      FOLDER_SPACING,
1628                                      folderxpm, folderxpmmask,
1629                                      folderopenxpm, folderopenxpmmask,
1630                                      FALSE, FALSE);
1631         gtk_ctree_expand(ctree, parent_node);
1632         gtk_ctree_node_set_row_data(ctree, node, item);
1633         if (normal_style)
1634                 gtk_ctree_node_set_row_style(ctree, node, normal_style);
1635         folderview_sort_folders(folderview, folderview->selected, item->folder);
1636
1637         gtk_clist_thaw(GTK_CLIST(ctree));
1638 }
1639
1640 static void folderview_search_cb(FolderView *folderview, guint action,
1641                                  GtkWidget *widget)
1642 {
1643         summary_search(folderview->summaryview);
1644 }
1645
1646 static void folderview_property_cb(FolderView *folderview, guint action,
1647                                    GtkWidget *widget)
1648 {
1649         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1650         FolderItem *item;
1651
1652         if (!folderview->selected) return;
1653
1654         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1655         g_return_if_fail(item != NULL);
1656         g_return_if_fail(item->folder != NULL);
1657
1658         if (folder_item_parent(item) == NULL && item->folder->account)
1659                 account_open(item->folder->account);
1660         else {
1661                 prefs_folder_item_open(item);
1662         }
1663 }
1664
1665 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
1666 {
1667         GSList *list = NULL;
1668         GSList *done = NULL;
1669         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1670         
1671         for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
1672                 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
1673                 &&  list->data != node) {
1674                         gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
1675                         done = g_slist_append(done, GTK_CTREE_NODE(list->data));
1676                 }
1677         }
1678         for (list = done; list != NULL; list = g_slist_next(list)) {
1679                 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse, 
1680                                                                  list->data);
1681         }
1682         g_slist_free(done);
1683 }
1684
1685 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
1686                             FolderItem *to_folder)
1687 {
1688         FolderItem *from_parent = NULL;
1689         FolderItem *new_folder = NULL;
1690         GtkCTreeNode *src_node = NULL;
1691         gchar *buf;
1692         gint status;
1693
1694         g_return_if_fail(folderview != NULL);
1695         g_return_if_fail(from_folder != NULL);
1696         g_return_if_fail(to_folder != NULL);
1697
1698         src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
1699         from_parent = folder_item_parent(from_folder);
1700         buf = g_strdup_printf(_("Moving %s to %s..."), from_folder->name, to_folder->name);
1701         STATUSBAR_PUSH(folderview->mainwin, buf);
1702         g_free(buf);
1703         summary_clear_all(folderview->summaryview);
1704         folderview->opened = NULL;
1705         folderview->selected = NULL;
1706         gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
1707         inc_lock();
1708         main_window_cursor_wait(folderview->mainwin);
1709         statusbar_verbosity_set(TRUE);
1710         folder_item_update_freeze();
1711         if ((status = folder_item_move_to(from_folder, to_folder, &new_folder)) == F_MOVE_OK) {
1712                 statusbar_verbosity_set(FALSE);
1713                 main_window_cursor_normal(folderview->mainwin);
1714                 STATUSBAR_POP(folderview->mainwin);
1715                 folder_item_update_thaw();
1716                 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
1717
1718                 folderview_sort_folders(folderview, 
1719                         gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), 
1720                                 NULL, folder_item_parent(new_folder)), new_folder->folder);
1721                 folderview_select(folderview, new_folder);
1722         } else {
1723                 statusbar_verbosity_set(FALSE);         
1724                 main_window_cursor_normal(folderview->mainwin);
1725                 STATUSBAR_POP(folderview->mainwin);
1726                 folder_item_update_thaw();
1727                 switch (status) {
1728                 case F_MOVE_FAILED_DEST_IS_PARENT:
1729                         alertpanel_error(_("Source and destination are the same."));
1730                         break;
1731                 case F_MOVE_FAILED_DEST_IS_CHILD:
1732                         alertpanel_error(_("Can't move a folder to one of its children."));
1733                         break;
1734                 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
1735                         alertpanel_error(_("Folder moving cannot be done between different mailboxes."));
1736                         break;
1737                 default:
1738                         alertpanel_error(_("Move failed!"));
1739                         break;
1740                 }
1741         }       
1742         inc_unlock();           
1743         gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
1744 }
1745
1746 static gint folderview_clist_compare(GtkCList *clist,
1747                                      gconstpointer ptr1, gconstpointer ptr2)
1748 {
1749         FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
1750         FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
1751
1752         if (!item1->name)
1753                 return (item2->name != NULL);
1754         if (!item2->name)
1755                 return -1;
1756
1757         return g_strcasecmp(item1->name, item2->name);
1758 }
1759
1760 static void folderview_processing_cb(FolderView *folderview, guint action,
1761                                      GtkWidget *widget)
1762 {
1763         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1764         FolderItem *item;
1765
1766         if (!folderview->selected) return;
1767
1768         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1769         g_return_if_fail(item != NULL);
1770         g_return_if_fail(item->folder != NULL);
1771
1772         prefs_filtering_open(&item->prefs->processing,
1773                              _("Processing configuration"), NULL, NULL);
1774 }
1775
1776 void folderview_set_target_folder_color(gint color_op) 
1777 {
1778         gint firstone = 1;
1779         GList *list;
1780         FolderView *folderview;
1781
1782         for (list = folderview_list; list != NULL; list = list->next) {
1783                 folderview = (FolderView *)list->data;
1784                 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
1785                 if (firstone) {
1786                         bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
1787                                 folderview->color_op;
1788                         firstone = 0;
1789                 }
1790         }
1791 }
1792
1793 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
1794 {
1795         folderview_init(folderview);
1796         folderview_set_all();
1797 }
1798
1799 static void drag_state_stop(FolderView *folderview)
1800 {
1801         if (folderview->drag_timer)
1802                 gtk_timeout_remove(folderview->drag_timer);
1803         folderview->drag_timer = 0;
1804         folderview->drag_node = NULL;
1805 }
1806
1807 static gint folderview_defer_expand(FolderView *folderview)
1808 {
1809         if (folderview->drag_node) {
1810                 folderview_recollapse_nodes(folderview, folderview->drag_node);
1811                 if (folderview->drag_item->collapsed) {
1812                         gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
1813                         folderview->nodes_to_recollapse = g_slist_append
1814                                 (folderview->nodes_to_recollapse, folderview->drag_node);
1815                 }
1816         }
1817         folderview->drag_item  = NULL;
1818         folderview->drag_timer = 0;
1819         return FALSE;
1820 }
1821
1822 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
1823 {
1824         /* the idea is that we call drag_state_start() whenever we want expansion to
1825          * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
1826          * we need to call drag_state_stop() */
1827         drag_state_stop(folderview);
1828         /* request expansion */
1829         if (0 != (folderview->drag_timer = gtk_timeout_add
1830                         (prefs_common.hover_timeout, 
1831                          (GtkFunction)folderview_defer_expand,
1832                          folderview))) {
1833                 folderview->drag_node = node;
1834                 folderview->drag_item = item;
1835         }                        
1836 }
1837
1838 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
1839                                   FolderView       *folderview)
1840 {
1841         GdkDragContext *context;
1842
1843         g_return_if_fail(folderview != NULL);
1844         if (folderview->selected == NULL) return;
1845         if (folderview->nodes_to_recollapse) 
1846                 g_slist_free(folderview->nodes_to_recollapse);
1847         folderview->nodes_to_recollapse = NULL;
1848         context = gtk_drag_begin(widget, folderview->target_list,
1849                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
1850         gtk_drag_set_icon_default(context);
1851 }
1852
1853 static void folderview_drag_data_get(GtkWidget        *widget,
1854                                      GdkDragContext   *drag_context,
1855                                      GtkSelectionData *selection_data,
1856                                      guint             info,
1857                                      guint             time,
1858                                      FolderView       *folderview)
1859 {
1860         FolderItem *item;
1861         GList *cur;
1862         gchar *source=NULL;
1863         
1864         for (cur = GTK_CLIST(folderview->ctree)->selection;
1865              cur != NULL; cur = cur->next) {
1866                 item = gtk_ctree_node_get_row_data
1867                         (GTK_CTREE(folderview->ctree), 
1868                          GTK_CTREE_NODE(cur->data));
1869                 if (item) {
1870                         source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
1871                         gtk_selection_data_set(selection_data,
1872                                                selection_data->target, 8,
1873                                                source, strlen(source));
1874                         break;
1875                 } else
1876                         return;
1877         }
1878 }
1879
1880 gboolean folderview_update_folder(gpointer source, gpointer userdata)
1881 {
1882         FolderUpdateData *hookdata;
1883         FolderView *folderview;
1884         GtkWidget *ctree;
1885
1886         hookdata = source;
1887         folderview = (FolderView *) userdata;   
1888         g_return_val_if_fail(hookdata != NULL, FALSE);
1889         g_return_val_if_fail(folderview != NULL, FALSE);
1890
1891         ctree = folderview->ctree;
1892         g_return_val_if_fail(ctree != NULL, FALSE);
1893
1894         if (hookdata->update_flags & FOLDER_NEW_FOLDERITEM)
1895                 folderview_create_folder_node(folderview, hookdata->item);
1896         else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
1897                 GtkCTreeNode *node;
1898
1899                 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
1900                 if (node != NULL)
1901                         gtk_ctree_remove_node(GTK_CTREE(ctree), node);
1902         } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_NEW_FOLDER | FOLDER_DESTROY_FOLDER))
1903                 folderview_set(folderview);
1904
1905         return FALSE;
1906 }
1907
1908 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
1909                                           GdkDragContext *context,
1910                                           gint            x,
1911                                           gint            y,
1912                                           guint           time,
1913                                           FolderView     *folderview)
1914 {
1915         gint row, column;
1916         FolderItem *item, *src_item = NULL;
1917         GtkCTreeNode *node = NULL;
1918         gboolean acceptable = FALSE;
1919         gint height = folderview->ctree->allocation.height;
1920         gint total_height = folderview->ctree->requisition.height;
1921         GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
1922                                 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1923         gfloat vpos = pos->value;
1924
1925         if (gtk_clist_get_selection_info
1926                 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
1927                 if (y > height - 24 && height + vpos < total_height)
1928                         gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
1929
1930                 if (y < 24 && y > 0)
1931                         gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
1932
1933                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
1934                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
1935                 src_item = folderview->summaryview->folder_item;
1936
1937                 if (item && item->folder && item->path &&
1938                     src_item && src_item != item) {
1939                         switch (FOLDER_TYPE(item->folder)) {
1940                         case F_MH:
1941 #if 0
1942                         case F_MBOX:
1943 #endif
1944                         case F_IMAP:
1945                                 acceptable = TRUE;
1946                                 break;
1947                         default:
1948                                 break;
1949                         }
1950                 } else if (item && item->folder && folder_item_get_path(item) &&
1951                            src_item && src_item != item) {
1952                         /* a root folder - acceptable only from folderview */
1953                         if (FOLDER_TYPE(item->folder) == F_MH || FOLDER_TYPE(item->folder) == F_IMAP)
1954                                 acceptable = TRUE;
1955                 }
1956                         
1957         }
1958
1959         if (acceptable || (src_item && src_item == item))
1960                 drag_state_start(folderview, node, item);
1961         
1962         if (acceptable) {
1963                 gtk_signal_handler_block_by_func
1964                         (GTK_OBJECT(widget),
1965                          GTK_SIGNAL_FUNC(folderview_selected), folderview);
1966                 gtk_ctree_select(GTK_CTREE(widget), node);
1967                 gtk_signal_handler_unblock_by_func
1968                         (GTK_OBJECT(widget),
1969                          GTK_SIGNAL_FUNC(folderview_selected), folderview);
1970                 gdk_drag_status(context, 
1971                                         (context->actions == GDK_ACTION_COPY ?
1972                                         GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
1973         } else {
1974                 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
1975                 gdk_drag_status(context, 0, time);
1976         }
1977
1978         return acceptable;
1979 }
1980
1981 static void folderview_drag_leave_cb(GtkWidget      *widget,
1982                                      GdkDragContext *context,
1983                                      guint           time,
1984                                      FolderView     *folderview)
1985 {
1986         drag_state_stop(folderview);
1987         gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
1988 }
1989
1990 static void folderview_drag_received_cb(GtkWidget        *widget,
1991                                         GdkDragContext   *drag_context,
1992                                         gint              x,
1993                                         gint              y,
1994                                         GtkSelectionData *data,
1995                                         guint             info,
1996                                         guint             time,
1997                                         FolderView       *folderview)
1998 {
1999         gint row, column;
2000         FolderItem *item, *src_item;
2001         GtkCTreeNode *node;
2002
2003         drag_state_stop(folderview);
2004         if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2005                 /* comes from summaryview */
2006                 if (gtk_clist_get_selection_info
2007                         (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
2008                         return;
2009
2010                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2011                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2012                 src_item = folderview->summaryview->folder_item;
2013                 
2014                 /* re-check (due to acceptable possibly set for folder moves */
2015                 if (!(item && item->folder && item->path &&
2016                       src_item && src_item != item && 
2017                       (FOLDER_TYPE(item->folder) == F_MH || FOLDER_TYPE(item->folder) == F_IMAP))) {
2018                         return;
2019                 }
2020                 if (item && src_item) {
2021                         switch (drag_context->action) {
2022                                 case GDK_ACTION_COPY:
2023                                         summary_copy_selected_to(folderview->summaryview, item);
2024                                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
2025                                         break;
2026                                 case GDK_ACTION_MOVE:
2027                                 case GDK_ACTION_DEFAULT:
2028                                 default:
2029                         if (FOLDER_TYPE(src_item->folder) == F_NEWS)
2030                                 summary_copy_selected_to(folderview->summaryview, item);
2031                         else
2032                                 summary_move_selected_to(folderview->summaryview, item);
2033                         gtk_drag_finish(drag_context, TRUE, TRUE, time);
2034                         }
2035                 } else
2036                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
2037         } else {
2038                 /* comes from folderview */
2039                 char *source;
2040                 
2041                 source = data->data + 17;
2042                 if (gtk_clist_get_selection_info
2043                     (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0
2044                     || *source == 0) {
2045                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
2046                         return;
2047                 }
2048                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2049                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2050                 src_item = folder_find_item_from_identifier(source);
2051
2052                 if (!item || !src_item || src_item->stype != F_NORMAL) {
2053                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
2054                         return;
2055                 }
2056
2057                 folderview_move_folder(folderview, src_item, item);
2058                 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2059         }
2060         folderview->nodes_to_recollapse = NULL;
2061 }
2062
2063 static void folderview_drag_end_cb(GtkWidget        *widget, 
2064                                    GdkDragContext   *drag_context,
2065                                    FolderView       *folderview)
2066 {
2067         drag_state_stop(folderview);
2068         g_slist_free(folderview->nodes_to_recollapse);
2069         folderview->nodes_to_recollapse = NULL;
2070 }
2071
2072 FolderItem *folderview_get_selected(FolderView *folderview)
2073 {
2074         return (FolderItem *) gtk_ctree_node_get_row_data(
2075                 GTK_CTREE(folderview->ctree), folderview->selected);
2076 }
2077
2078 void folderview_register_popup(FolderViewPopup *fpopup)
2079 {
2080         GList *folderviews;
2081
2082         for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
2083                 FolderView *folderview = folderviews->data;
2084                 GtkItemFactory *factory;
2085
2086                 factory = create_ifactory(folderview, fpopup);
2087                 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
2088         }       
2089         g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
2090 }
2091
2092 void folderview_unregister_popup(FolderViewPopup *fpopup)
2093 {
2094         GList *folderviews;
2095
2096         for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
2097                 FolderView *folderview = folderviews->data;
2098
2099                 g_hash_table_remove(folderview->popups, fpopup->klass);
2100         }       
2101         g_hash_table_remove(folderview_popups, fpopup->klass);
2102 }