* src/folderview.c
[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                 folderview->open_folder = FALSE;
1516                 can_select = TRUE;
1517
1518                 return;
1519         }
1520
1521         main_window_cursor_normal(folderview->mainwin);
1522
1523         /* Show messages */
1524         summary_set_prefs_from_folderitem(folderview->summaryview, item);
1525         opened = summary_show(folderview->summaryview, item);
1526         
1527         folder_clean_cache_memory();
1528
1529         if (!opened) {
1530                 gtkut_ctree_set_focus_row(ctree, folderview->opened);
1531                 gtk_ctree_select(ctree, folderview->opened);
1532         } else {
1533                 folderview->opened = row;
1534                 if (gtk_ctree_node_is_visible(ctree, row)
1535                     != GTK_VISIBILITY_FULL)
1536                         gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
1537         }
1538
1539         STATUSBAR_POP(folderview->mainwin);
1540
1541         folderview->open_folder = FALSE;
1542         can_select = TRUE;
1543 }
1544
1545 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
1546                                      FolderView *folderview)
1547 {
1548         FolderItem *item;
1549
1550         item = gtk_ctree_node_get_row_data(ctree, node);
1551         g_return_if_fail(item != NULL);
1552         item->collapsed = FALSE;
1553         folderview_update_node(folderview, node);
1554 }
1555
1556 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
1557                                       FolderView *folderview)
1558 {
1559         FolderItem *item;
1560
1561         item = gtk_ctree_node_get_row_data(ctree, node);
1562         g_return_if_fail(item != NULL);
1563         item->collapsed = TRUE;
1564         folderview_update_node(folderview, node);
1565 }
1566
1567 static void folderview_popup_close(GtkMenuShell *menu_shell,
1568                                    FolderView *folderview)
1569 {
1570         if (!folderview->opened) return;
1571
1572         gtkut_ctree_set_focus_row(GTK_CTREE(folderview->ctree),
1573                                   folderview->opened);
1574         gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
1575 }
1576
1577 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
1578                                    FolderView *folderview)
1579 {
1580         switch (column) {
1581         case COL_FOLDER:
1582                 prefs_common.folder_col_folder = width;
1583                 break;
1584         case COL_NEW:
1585                 prefs_common.folder_col_new = width;
1586                 break;
1587         case COL_UNREAD:
1588                 prefs_common.folder_col_unread = width;
1589                 break;
1590         case COL_TOTAL:
1591                 prefs_common.folder_col_total = width;
1592                 break;
1593         default:
1594                 break;
1595         }
1596 }
1597
1598 void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
1599 {
1600         GNode *srcnode;
1601
1602         folderview_create_folder_node(folderview, item);
1603         
1604         srcnode = item->folder->node;   
1605         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1606         srcnode = srcnode->children;
1607         while (srcnode != NULL) {
1608                 if (srcnode && srcnode->data) {
1609                         FolderItem *next_item = (FolderItem*) srcnode->data;
1610                         folderview_create_folder_node_recursive(folderview, next_item);
1611                 }
1612                 srcnode = srcnode->next;
1613         }
1614 }
1615
1616 void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
1617 {
1618         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1619         gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
1620         GtkCTreeNode *node, *parent_node;
1621         
1622         parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
1623         if (parent_node == NULL)
1624                 return;
1625
1626         gtk_clist_freeze(GTK_CLIST(ctree));
1627
1628         text[COL_FOLDER] = item->name;
1629         node = gtk_ctree_insert_node(ctree, parent_node, NULL, text,
1630                                      FOLDER_SPACING,
1631                                      folderxpm, folderxpmmask,
1632                                      folderopenxpm, folderopenxpmmask,
1633                                      FALSE, FALSE);
1634         gtk_ctree_expand(ctree, parent_node);
1635         gtk_ctree_node_set_row_data(ctree, node, item);
1636         if (normal_style)
1637                 gtk_ctree_node_set_row_style(ctree, node, normal_style);
1638         folderview_sort_folders(folderview, folderview->selected, item->folder);
1639
1640         gtk_clist_thaw(GTK_CLIST(ctree));
1641 }
1642
1643 static void folderview_search_cb(FolderView *folderview, guint action,
1644                                  GtkWidget *widget)
1645 {
1646         summary_search(folderview->summaryview);
1647 }
1648
1649 static void folderview_property_cb(FolderView *folderview, guint action,
1650                                    GtkWidget *widget)
1651 {
1652         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1653         FolderItem *item;
1654
1655         if (!folderview->selected) return;
1656
1657         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1658         g_return_if_fail(item != NULL);
1659         g_return_if_fail(item->folder != NULL);
1660
1661         if (folder_item_parent(item) == NULL && item->folder->account)
1662                 account_open(item->folder->account);
1663         else {
1664                 prefs_folder_item_open(item);
1665         }
1666 }
1667
1668 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
1669 {
1670         GSList *list = NULL;
1671         GSList *done = NULL;
1672         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1673         
1674         for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
1675                 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
1676                 &&  list->data != node) {
1677                         gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
1678                         done = g_slist_append(done, GTK_CTREE_NODE(list->data));
1679                 }
1680         }
1681         for (list = done; list != NULL; list = g_slist_next(list)) {
1682                 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse, 
1683                                                                  list->data);
1684         }
1685         g_slist_free(done);
1686 }
1687
1688 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
1689                             FolderItem *to_folder)
1690 {
1691         FolderItem *from_parent = NULL;
1692         FolderItem *new_folder = NULL;
1693         GtkCTreeNode *src_node = NULL;
1694         gchar *buf;
1695         gint status;
1696
1697         g_return_if_fail(folderview != NULL);
1698         g_return_if_fail(from_folder != NULL);
1699         g_return_if_fail(to_folder != NULL);
1700
1701         src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
1702         from_parent = folder_item_parent(from_folder);
1703         buf = g_strdup_printf(_("Moving %s to %s..."), from_folder->name, to_folder->name);
1704         STATUSBAR_PUSH(folderview->mainwin, buf);
1705         g_free(buf);
1706         summary_clear_all(folderview->summaryview);
1707         folderview->opened = NULL;
1708         folderview->selected = NULL;
1709         gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
1710         inc_lock();
1711         main_window_cursor_wait(folderview->mainwin);
1712         statusbar_verbosity_set(TRUE);
1713         folder_item_update_freeze();
1714         if ((status = folder_item_move_to(from_folder, to_folder, &new_folder)) == F_MOVE_OK) {
1715                 statusbar_verbosity_set(FALSE);
1716                 main_window_cursor_normal(folderview->mainwin);
1717                 STATUSBAR_POP(folderview->mainwin);
1718                 folder_item_update_thaw();
1719                 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
1720
1721                 folderview_sort_folders(folderview, 
1722                         gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), 
1723                                 NULL, folder_item_parent(new_folder)), new_folder->folder);
1724                 folderview_select(folderview, new_folder);
1725         } else {
1726                 statusbar_verbosity_set(FALSE);         
1727                 main_window_cursor_normal(folderview->mainwin);
1728                 STATUSBAR_POP(folderview->mainwin);
1729                 folder_item_update_thaw();
1730                 switch (status) {
1731                 case F_MOVE_FAILED_DEST_IS_PARENT:
1732                         alertpanel_error(_("Source and destination are the same."));
1733                         break;
1734                 case F_MOVE_FAILED_DEST_IS_CHILD:
1735                         alertpanel_error(_("Can't move a folder to one of its children."));
1736                         break;
1737                 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
1738                         alertpanel_error(_("Folder moving cannot be done between different mailboxes."));
1739                         break;
1740                 default:
1741                         alertpanel_error(_("Move failed!"));
1742                         break;
1743                 }
1744         }       
1745         inc_unlock();           
1746         gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
1747 }
1748
1749 static gint folderview_clist_compare(GtkCList *clist,
1750                                      gconstpointer ptr1, gconstpointer ptr2)
1751 {
1752         FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
1753         FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
1754
1755         if (!item1->name)
1756                 return (item2->name != NULL);
1757         if (!item2->name)
1758                 return -1;
1759
1760         return g_strcasecmp(item1->name, item2->name);
1761 }
1762
1763 static void folderview_processing_cb(FolderView *folderview, guint action,
1764                                      GtkWidget *widget)
1765 {
1766         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1767         FolderItem *item;
1768
1769         if (!folderview->selected) return;
1770
1771         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
1772         g_return_if_fail(item != NULL);
1773         g_return_if_fail(item->folder != NULL);
1774
1775         prefs_filtering_open(&item->prefs->processing,
1776                              _("Processing configuration"), NULL, NULL);
1777 }
1778
1779 void folderview_set_target_folder_color(gint color_op) 
1780 {
1781         gint firstone = 1;
1782         GList *list;
1783         FolderView *folderview;
1784
1785         for (list = folderview_list; list != NULL; list = list->next) {
1786                 folderview = (FolderView *)list->data;
1787                 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
1788                 if (firstone) {
1789                         bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
1790                                 folderview->color_op;
1791                         firstone = 0;
1792                 }
1793         }
1794 }
1795
1796 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
1797 {
1798         folderview_init(folderview);
1799         folderview_set_all();
1800 }
1801
1802 static void drag_state_stop(FolderView *folderview)
1803 {
1804         if (folderview->drag_timer)
1805                 gtk_timeout_remove(folderview->drag_timer);
1806         folderview->drag_timer = 0;
1807         folderview->drag_node = NULL;
1808 }
1809
1810 static gint folderview_defer_expand(FolderView *folderview)
1811 {
1812         if (folderview->drag_node) {
1813                 folderview_recollapse_nodes(folderview, folderview->drag_node);
1814                 if (folderview->drag_item->collapsed) {
1815                         gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
1816                         folderview->nodes_to_recollapse = g_slist_append
1817                                 (folderview->nodes_to_recollapse, folderview->drag_node);
1818                 }
1819         }
1820         folderview->drag_item  = NULL;
1821         folderview->drag_timer = 0;
1822         return FALSE;
1823 }
1824
1825 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
1826 {
1827         /* the idea is that we call drag_state_start() whenever we want expansion to
1828          * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
1829          * we need to call drag_state_stop() */
1830         drag_state_stop(folderview);
1831         /* request expansion */
1832         if (0 != (folderview->drag_timer = gtk_timeout_add
1833                         (prefs_common.hover_timeout, 
1834                          (GtkFunction)folderview_defer_expand,
1835                          folderview))) {
1836                 folderview->drag_node = node;
1837                 folderview->drag_item = item;
1838         }                        
1839 }
1840
1841 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
1842                                   FolderView       *folderview)
1843 {
1844         GdkDragContext *context;
1845
1846         g_return_if_fail(folderview != NULL);
1847         if (folderview->selected == NULL) return;
1848         if (folderview->nodes_to_recollapse) 
1849                 g_slist_free(folderview->nodes_to_recollapse);
1850         folderview->nodes_to_recollapse = NULL;
1851         context = gtk_drag_begin(widget, folderview->target_list,
1852                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
1853         gtk_drag_set_icon_default(context);
1854 }
1855
1856 static void folderview_drag_data_get(GtkWidget        *widget,
1857                                      GdkDragContext   *drag_context,
1858                                      GtkSelectionData *selection_data,
1859                                      guint             info,
1860                                      guint             time,
1861                                      FolderView       *folderview)
1862 {
1863         FolderItem *item;
1864         GList *cur;
1865         gchar *source = NULL;
1866
1867         for (cur = GTK_CLIST(folderview->ctree)->selection;
1868              cur != NULL; cur = cur->next) {
1869                 item = gtk_ctree_node_get_row_data
1870                         (GTK_CTREE(folderview->ctree), 
1871                          GTK_CTREE_NODE(cur->data));
1872                 if (item) {
1873                         source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
1874                         gtk_selection_data_set(selection_data,
1875                                                selection_data->target, 8,
1876                                                source, strlen(source));
1877                         break;
1878                 } else
1879                         return;
1880         }
1881 }
1882
1883 gboolean folderview_update_folder(gpointer source, gpointer userdata)
1884 {
1885         FolderUpdateData *hookdata;
1886         FolderView *folderview;
1887         GtkWidget *ctree;
1888
1889         hookdata = source;
1890         folderview = (FolderView *) userdata;   
1891         g_return_val_if_fail(hookdata != NULL, FALSE);
1892         g_return_val_if_fail(folderview != NULL, FALSE);
1893
1894         ctree = folderview->ctree;
1895         g_return_val_if_fail(ctree != NULL, FALSE);
1896
1897         if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
1898                 folderview_create_folder_node(folderview, hookdata->item);
1899         else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
1900                 GtkCTreeNode *node;
1901
1902                 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
1903                 if (node != NULL)
1904                         gtk_ctree_remove_node(GTK_CTREE(ctree), node);
1905         } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
1906                 folderview_set(folderview);
1907
1908         return FALSE;
1909 }
1910
1911 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
1912                                           GdkDragContext *context,
1913                                           gint            x,
1914                                           gint            y,
1915                                           guint           time,
1916                                           FolderView     *folderview)
1917 {
1918         gint row, column;
1919         FolderItem *item, *src_item = NULL;
1920         GtkCTreeNode *node = NULL;
1921         gboolean acceptable = FALSE;
1922         gint height = folderview->ctree->allocation.height;
1923         gint total_height = folderview->ctree->requisition.height;
1924         GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
1925                                 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1926         gfloat vpos = pos->value;
1927
1928         if (gtk_clist_get_selection_info
1929                 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
1930                 GtkWidget *srcwidget;
1931
1932                 if (y > height - 24 && height + vpos < total_height)
1933                         gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
1934
1935                 if (y < 24 && y > 0)
1936                         gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
1937
1938                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
1939                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
1940                 src_item = folderview->summaryview->folder_item;
1941
1942                 srcwidget = gtk_drag_get_source_widget(context);
1943                 if (srcwidget == folderview->summaryview->ctree) {
1944                         /* comes from summaryview */
1945                         /* we are copying messages, so only accept folder items that are not
1946                            the source item, are no root items and can copy messages */
1947                         if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
1948                             src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)
1949                                 acceptable = TRUE;
1950                 } else if (srcwidget == folderview->ctree) {
1951                         /* comes from folderview */
1952                         /* we are moving folder items, only accept folders that are not
1953                            the source items and can copy messages and create folder items */
1954                         if (item && item->folder && src_item && src_item != item &&
1955                             FOLDER_CLASS(item->folder)->copy_msg != NULL &&
1956                             FOLDER_CLASS(item->folder)->create_folder != NULL)
1957                                 acceptable = TRUE;
1958                 }
1959         }
1960
1961         if (acceptable || (src_item && src_item == item))
1962                 drag_state_start(folderview, node, item);
1963         
1964         if (acceptable) {
1965                 gtk_signal_handler_block_by_func
1966                         (GTK_OBJECT(widget),
1967                          GTK_SIGNAL_FUNC(folderview_selected), folderview);
1968                 gtk_ctree_select(GTK_CTREE(widget), node);
1969                 gtk_signal_handler_unblock_by_func
1970                         (GTK_OBJECT(widget),
1971                          GTK_SIGNAL_FUNC(folderview_selected), folderview);
1972                 gdk_drag_status(context, 
1973                                         (context->actions == GDK_ACTION_COPY ?
1974                                         GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
1975         } else {
1976                 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
1977                 gdk_drag_status(context, 0, time);
1978         }
1979
1980         return acceptable;
1981 }
1982
1983 static void folderview_drag_leave_cb(GtkWidget      *widget,
1984                                      GdkDragContext *context,
1985                                      guint           time,
1986                                      FolderView     *folderview)
1987 {
1988         drag_state_stop(folderview);
1989         gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
1990 }
1991
1992 static void folderview_drag_received_cb(GtkWidget        *widget,
1993                                         GdkDragContext   *drag_context,
1994                                         gint              x,
1995                                         gint              y,
1996                                         GtkSelectionData *data,
1997                                         guint             info,
1998                                         guint             time,
1999                                         FolderView       *folderview)
2000 {
2001         gint row, column;
2002         FolderItem *item, *src_item;
2003         GtkCTreeNode *node;
2004
2005         drag_state_stop(folderview);
2006         if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2007                 /* comes from summaryview */
2008                 if (gtk_clist_get_selection_info
2009                         (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
2010                         return;
2011
2012                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2013                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2014                 src_item = folderview->summaryview->folder_item;
2015                 
2016                 /* re-check (due to acceptable possibly set for folder moves */
2017                 if (!(item && item->folder && item->path &&
2018                       src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2019                         return;
2020                 }
2021                 if (item && src_item) {
2022                         switch (drag_context->action) {
2023                         case GDK_ACTION_COPY:
2024                                 summary_copy_selected_to(folderview->summaryview, item);
2025                                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2026                                 break;
2027                         case GDK_ACTION_MOVE:
2028                         case GDK_ACTION_DEFAULT:
2029                         default:
2030                                 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
2031                                         summary_copy_selected_to(folderview->summaryview, item);
2032                                 else
2033                                         summary_move_selected_to(folderview->summaryview, item);
2034                                 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2035                         }
2036                 } else
2037                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
2038         } else {
2039                 /* comes from folderview */
2040                 char *source;
2041                 
2042                 source = data->data + 17;
2043                 if (gtk_clist_get_selection_info
2044                     (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0
2045                     || *source == 0) {
2046                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
2047                         return;
2048                 }
2049                 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2050                 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2051                 src_item = folder_find_item_from_identifier(source);
2052
2053                 if (!item || !src_item || src_item->stype != F_NORMAL) {
2054                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
2055                         return;
2056                 }
2057
2058                 folderview_move_folder(folderview, src_item, item);
2059                 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2060         }
2061         folderview->nodes_to_recollapse = NULL;
2062 }
2063
2064 static void folderview_drag_end_cb(GtkWidget        *widget, 
2065                                    GdkDragContext   *drag_context,
2066                                    FolderView       *folderview)
2067 {
2068         drag_state_stop(folderview);
2069         g_slist_free(folderview->nodes_to_recollapse);
2070         folderview->nodes_to_recollapse = NULL;
2071 }
2072
2073 FolderItem *folderview_get_selected(FolderView *folderview)
2074 {
2075         return (FolderItem *) gtk_ctree_node_get_row_data(
2076                 GTK_CTREE(folderview->ctree), folderview->selected);
2077 }
2078
2079 void folderview_register_popup(FolderViewPopup *fpopup)
2080 {
2081         GList *folderviews;
2082
2083         for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
2084                 FolderView *folderview = folderviews->data;
2085                 GtkItemFactory *factory;
2086
2087                 factory = create_ifactory(folderview, fpopup);
2088                 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
2089         }       
2090         g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
2091 }
2092
2093 void folderview_unregister_popup(FolderViewPopup *fpopup)
2094 {
2095         GList *folderviews;
2096
2097         for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
2098                 FolderView *folderview = folderviews->data;
2099
2100                 g_hash_table_remove(folderview->popups, fpopup->klass);
2101         }       
2102         g_hash_table_remove(folderview_popups, fpopup->klass);
2103 }