Further improve API isolation of folderview.
[claws.git] / src / folderview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtk.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include "main.h"
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
38 #include "menu.h"
39 #include "stock_pixmap.h"
40 #include "procmsg.h"
41 #include "utils.h"
42 #include "gtkutils.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
47 #include "account.h"
48 #include "folder.h"
49 #include "foldersel.h"
50 #include "inc.h"
51 #include "statusbar.h"
52 #include "hooks.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
58 #include "manual.h"
59 #include "timing.h"
60 #include "log.h"
61 #include "gtkcmctree.h"
62
63 #define COL_FOLDER_WIDTH        150
64 #define COL_NUM_WIDTH           32
65
66 static GList *folderview_list = NULL;
67
68 static GtkStyle *normal_style;
69 static GtkStyle *normal_color_style;
70 static GtkStyle *bold_style;
71 static GtkStyle *bold_color_style;
72 static GtkStyle *bold_tgtfold_style;
73
74 static GdkPixbuf *inboxxpm;
75 static GdkPixbuf *inboxhrmxpm;
76 static GdkPixbuf *inboxopenxpm;
77 static GdkPixbuf *inboxopenhrmxpm;
78 static GdkPixbuf *outboxxpm;
79 static GdkPixbuf *outboxhrmxpm;
80 static GdkPixbuf *outboxopenxpm;
81 static GdkPixbuf *outboxopenhrmxpm;
82 static GdkPixbuf *folderxpm;
83 static GdkPixbuf *folderhrmxpm;
84 static GdkPixbuf *folderopenxpm;
85 static GdkPixbuf *folderopenhrmxpm;
86 static GdkPixbuf *trashopenxpm;
87 static GdkPixbuf *trashopenhrmxpm;
88 static GdkPixbuf *trashxpm;
89 static GdkPixbuf *trashhrmxpm;
90 static GdkPixbuf *queuexpm;
91 static GdkPixbuf *queuehrmxpm;
92 static GdkPixbuf *queueopenxpm;
93 static GdkPixbuf *queueopenhrmxpm;
94 static GdkPixbuf *draftsxpm;
95 static GdkPixbuf *draftsopenxpm;
96 static GdkPixbuf *noselectxpm;
97
98 static GdkPixbuf *m_inboxxpm;
99 static GdkPixbuf *m_inboxhrmxpm;
100 static GdkPixbuf *m_inboxopenxpm;
101 static GdkPixbuf *m_inboxopenhrmxpm;
102 static GdkPixbuf *m_outboxxpm;
103 static GdkPixbuf *m_outboxhrmxpm;
104 static GdkPixbuf *m_outboxopenxpm;
105 static GdkPixbuf *m_outboxopenhrmxpm;
106 static GdkPixbuf *m_folderxpm;
107 static GdkPixbuf *m_folderhrmxpm;
108 static GdkPixbuf *m_folderopenxpm;
109 static GdkPixbuf *m_folderopenhrmxpm;
110 static GdkPixbuf *m_trashopenxpm;
111 static GdkPixbuf *m_trashopenhrmxpm;
112 static GdkPixbuf *m_trashxpm;
113 static GdkPixbuf *m_trashhrmxpm;
114 static GdkPixbuf *m_queuexpm;
115 static GdkPixbuf *m_queuehrmxpm;
116 static GdkPixbuf *m_queueopenxpm;
117 static GdkPixbuf *m_queueopenhrmxpm;
118 static GdkPixbuf *m_draftsxpm;
119 static GdkPixbuf *m_draftsopenxpm;
120
121 static GdkPixbuf *newxpm;
122 static GdkPixbuf *unreadxpm;
123 static GdkPixbuf *readxpm;
124
125 static void folderview_select_node       (FolderView    *folderview,
126                                           GtkCMCTreeNode        *node);
127 static void folderview_set_folders       (FolderView    *folderview);
128 static void folderview_sort_folders      (FolderView    *folderview,
129                                           GtkCMCTreeNode        *root,
130                                           Folder        *folder);
131 static void folderview_append_folder     (FolderView    *folderview,
132                                           Folder        *folder);
133 static void folderview_update_node       (FolderView    *folderview,
134                                           GtkCMCTreeNode        *node);
135
136 static gint folderview_clist_compare    (GtkCMCList     *clist,
137                                          gconstpointer   ptr1,
138                                          gconstpointer   ptr2);
139
140 /* callback functions */
141 static gboolean folderview_button_pressed       (GtkWidget      *ctree,
142                                                  GdkEventButton *event,
143                                                  FolderView     *folderview);
144 static gboolean folderview_button_released      (GtkWidget      *ctree,
145                                                  GdkEventButton *event,
146                                                  FolderView     *folderview);
147 static gboolean folderview_key_pressed  (GtkWidget      *widget,
148                                          GdkEventKey    *event,
149                                          FolderView     *folderview);
150 static void folderview_selected         (GtkCMCTree     *ctree,
151                                          GtkCMCTreeNode *row,
152                                          gint            column,
153                                          FolderView     *folderview);
154 static void folderview_tree_expanded    (GtkCMCTree     *ctree,
155                                          GtkCMCTreeNode *node,
156                                          FolderView     *folderview);
157 static void folderview_tree_collapsed   (GtkCMCTree     *ctree,
158                                          GtkCMCTreeNode *node,
159                                          FolderView     *folderview);
160 static void folderview_popup_close      (GtkMenuShell   *menu_shell,
161                                          FolderView     *folderview);
162 static void folderview_col_resized      (GtkCMCList     *clist,
163                                          gint            column,
164                                          gint            width,
165                                          FolderView     *folderview);
166
167 static void mark_all_read_handler       (GtkAction      *action,
168                                          gpointer        data,
169                                          gboolean        recursive);
170
171 static void mark_all_read_cb            (GtkAction      *action,
172                                          gpointer        data);
173 static void mark_all_read_recursive_cb  (GtkAction      *action,
174                                          gpointer        data);
175
176 static void folderview_empty_trash_cb   (GtkAction      *action,
177                                          gpointer        data);
178
179 static void folderview_send_queue_cb    (GtkAction      *action,
180                                          gpointer        data);
181
182 static void folderview_search_cb        (GtkAction      *action,
183                                          gpointer        data);
184 static void folderview_run_processing_cb(GtkAction      *action,
185                                          gpointer        data);
186
187 static void folderview_property_cb      (GtkAction      *action,
188                                          gpointer        data);
189
190 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
191                                           GdkDragContext *context,
192                                           gint            x,
193                                           gint            y,
194                                           guint           time,
195                                           FolderView     *folderview);
196 static void folderview_drag_leave_cb     (GtkWidget        *widget,
197                                           GdkDragContext   *context,
198                                           guint             time,
199                                           FolderView       *folderview);
200 static void folderview_drag_received_cb  (GtkWidget        *widget,
201                                           GdkDragContext   *drag_context,
202                                           gint              x,
203                                           gint              y,
204                                           GtkSelectionData *data,
205                                           guint             info,
206                                           guint             time,
207                                           FolderView       *folderview);
208 #ifndef GENERIC_UMPC
209 static void folderview_start_drag        (GtkWidget *widget, gint button, GdkEvent *event,
210                                           FolderView       *folderview);
211 #endif
212 static void folderview_drag_data_get     (GtkWidget        *widget,
213                                           GdkDragContext   *drag_context,
214                                           GtkSelectionData *selection_data,
215                                           guint             info,
216                                           guint             time,
217                                           FolderView       *folderview);
218 static void folderview_drag_end_cb       (GtkWidget        *widget,
219                                           GdkDragContext   *drag_context,
220                                           FolderView       *folderview);
221
222 static void folderview_create_folder_node       (FolderView       *folderview, 
223                                           FolderItem       *item);
224 static gboolean folderview_update_folder         (gpointer          source,
225                                           gpointer          userdata);
226 static gboolean folderview_update_item_claws     (gpointer          source,
227                                           gpointer          data);
228 static void folderview_processing_cb(GtkAction *action, gpointer data);
229 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row, 
230                                 GdkEventButton *event);
231
232 GHashTable *folderview_popups;
233
234 static GtkActionEntry folderview_common_popup_entries[] = 
235 {
236         {"FolderViewPopup",                     NULL, "FolderViewPopup" },
237         {"FolderViewPopup/MarkAllRead",         NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
238         {"FolderViewPopup/MarkAllReadRec",      NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
239         {"FolderViewPopup/---",                 NULL, "---" },
240         {"FolderViewPopup/RunProcessing",       NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
241         {"FolderViewPopup/SearchFolder",        NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
242         {"FolderViewPopup/Properties",          NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
243         {"FolderViewPopup/Processing",          NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
244         {"FolderViewPopup/EmptyTrash",          NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
245         {"FolderViewPopup/SendQueue",           NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
246         
247 };
248
249 GtkTargetEntry folderview_drag_types[] =
250 {
251         {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
252         {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
253 };
254
255 void folderview_initialize(void)
256 {
257         FolderViewPopup *fpopup;
258
259         fpopup = g_new0(FolderViewPopup, 1);
260
261         fpopup->klass = "common";
262         fpopup->path = "<CommonFolder>";
263         fpopup->entries = folderview_common_popup_entries;
264         fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
265         fpopup->set_sensitivity = NULL;
266
267         folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
268         g_hash_table_insert(folderview_popups, "common", fpopup);
269 }
270
271 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
272 {
273         FolderViewPopup *fpopup_common;
274         GtkActionGroup *action_group;
275         
276         action_group = cm_menu_create_action_group(
277                                 fpopup->path, 
278                                 fpopup->entries, fpopup->n_entries, 
279                                 (gpointer)folderview);
280
281         if (fpopup->toggle_entries)
282                 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
283                                 fpopup->n_toggle_entries,
284                                 (gpointer)folderview);
285         if (fpopup->radio_entries)
286                 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
287                                 fpopup->n_radio_entries, fpopup->radio_default, 
288                                 G_CALLBACK(fpopup->radio_callback), 
289                                 (gpointer)folderview);
290
291         fpopup_common = g_hash_table_lookup(folderview_popups, "common");
292         if (fpopup_common != fpopup) {
293                 gtk_action_group_add_actions(action_group, fpopup_common->entries,
294                                 fpopup_common->n_entries,
295                                 (gpointer)folderview);
296                 if (fpopup_common->toggle_entries)
297                         gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
298                                         fpopup_common->n_toggle_entries,
299                                         (gpointer)folderview);
300                 if (fpopup_common->radio_entries)
301                         gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
302                                         fpopup_common->n_radio_entries, fpopup_common->radio_default, 
303                                         G_CALLBACK(fpopup_common->radio_callback), 
304                                         (gpointer)folderview);
305         }
306
307         return action_group;
308 }
309
310 static void create_action_groups(gpointer key, gpointer value, gpointer data)
311 {
312         FolderView *folderview = data;
313         FolderViewPopup *fpopup = value;
314         GtkActionGroup *group;
315
316         group = create_action_group(folderview, fpopup);
317         g_hash_table_insert(folderview->popups, fpopup->klass, group);
318 }
319
320 static void folderview_column_set_titles(FolderView *folderview)
321 {
322         GtkWidget *ctree = folderview->ctree;
323         GtkWidget *label_folder;
324         GtkWidget *label_new;
325         GtkWidget *label_unread;
326         GtkWidget *label_total;
327         GtkWidget *hbox_folder;
328         GtkWidget *hbox_new;
329         GtkWidget *hbox_unread;
330         GtkWidget *hbox_total;
331         gint *col_pos = folderview->col_pos;
332
333         debug_print("setting titles...\n");
334         gtk_widget_realize(folderview->ctree);
335         gtk_widget_show_all(folderview->scrolledwin);
336         
337         /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
338          * instead text (text overflows making them unreadable and ugly) */
339         stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
340         stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
341         stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
342         
343         label_folder = gtk_label_new(_("Folder"));
344         label_new = gtk_image_new_from_pixbuf(newxpm);
345         label_unread = gtk_image_new_from_pixbuf(unreadxpm);
346         label_total = gtk_image_new_from_pixbuf(readxpm);
347         
348         gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
349          
350         hbox_folder = gtk_hbox_new(FALSE, 4);
351         hbox_new = gtk_hbox_new(FALSE, 4);
352         hbox_unread = gtk_hbox_new(FALSE, 4);
353         hbox_total = gtk_hbox_new(FALSE, 4);
354
355         /* left justified */
356         gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
357         gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
358         gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
359         gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
360         gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
361         gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
362         gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
363         gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
364
365         gtk_widget_show_all(hbox_folder);
366         gtk_widget_show_all(hbox_new);
367         gtk_widget_show_all(hbox_unread);
368         gtk_widget_show_all(hbox_total);
369
370 #ifdef GENERIC_UMPC
371         gtk_widget_set_size_request(hbox_new, -1, 20);
372         gtk_widget_set_size_request(hbox_unread, -1, 20);
373         gtk_widget_set_size_request(hbox_total, -1, 20);
374 #endif
375
376         gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
377         gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
378         gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
379         gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
380
381 #ifdef GENERIC_UMPC
382         GTK_EVENTS_FLUSH();
383 #endif
384
385         gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
386         gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
387         gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
388 }
389
390 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
391 {
392         FolderView *folderview = (FolderView *)data;
393         GdkEventButton event;
394         if (folderview_get_selected_item(folderview) == NULL)
395                 return FALSE;
396         
397         event.button = 3;
398         event.time = gtk_get_current_event_time();
399         
400         folderview_set_sens_and_popup_menu(folderview, -1, 
401                                 &event);
402
403         return TRUE;
404 }
405
406
407 static GtkWidget *folderview_ctree_create(FolderView *folderview)
408 {
409         GtkWidget *ctree;
410         gint *col_pos;
411         FolderColumnState *col_state;
412         FolderColumnType type;
413         gchar *titles[N_FOLDER_COLS];
414         gint i;
415         GtkWidget *scrolledwin = folderview->scrolledwin;
416
417         debug_print("creating tree...\n");
418         memset(titles, 0, sizeof(titles));
419
420         col_state = prefs_folder_column_get_config();
421         memset(titles, 0, sizeof(titles));
422
423         col_pos = folderview->col_pos;
424
425         for (i = 0; i < N_FOLDER_COLS; i++) {
426                 folderview->col_state[i] = col_state[i];
427                 type = col_state[i].type;
428                 col_pos[type] = i;
429         }
430
431         titles[col_pos[F_COL_FOLDER]] = _("Folder");
432         titles[col_pos[F_COL_NEW]]    = _("New");
433         titles[col_pos[F_COL_UNREAD]] = _("Unread");
434         /* TRANSLATORS: This in Number sign in American style */
435         titles[col_pos[F_COL_TOTAL]]  = _("#");
436
437         ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
438                                            titles);
439
440         if (prefs_common.show_col_headers == FALSE)
441                 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
442
443
444         gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
445         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
446                                            GTK_JUSTIFY_RIGHT);
447         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
448                                            col_pos[F_COL_UNREAD],
449                                            GTK_JUSTIFY_RIGHT);
450         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), 
451                                            col_pos[F_COL_TOTAL],
452                                            GTK_JUSTIFY_RIGHT);
453         gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
454         gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
455                              GTK_CMCTREE_EXPANDER_TRIANGLE);
456
457         gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
458         gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
459
460         gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
461         gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
462
463         /* don't let title buttons take key focus */
464         for (i = 0; i < N_FOLDER_COLS; i++) {
465                 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
466                 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
467                                    prefs_common.folder_col_size[i]);
468                 gtk_cmclist_set_column_visibility
469                         (GTK_CMCLIST(ctree), i, col_state[i].visible);
470         }
471
472         g_signal_connect(G_OBJECT(ctree), "key_press_event",
473                          G_CALLBACK(folderview_key_pressed),
474                          folderview);
475         g_signal_connect(G_OBJECT(ctree), "button_press_event",
476                          G_CALLBACK(folderview_button_pressed),
477                          folderview);
478         g_signal_connect(G_OBJECT(ctree), "popup-menu",
479                          G_CALLBACK(folderview_popup_menu), folderview);
480         g_signal_connect(G_OBJECT(ctree), "button_release_event",
481                          G_CALLBACK(folderview_button_released),
482                          folderview);
483         g_signal_connect(G_OBJECT(ctree), "tree_select_row",
484                          G_CALLBACK(folderview_selected), folderview);
485 #ifndef GENERIC_UMPC
486         /* drag-n-dropping folders on maemo is impractical as this 
487          * opens the folder almost everytime */
488         g_signal_connect(G_OBJECT(ctree), "start_drag",
489                          G_CALLBACK(folderview_start_drag), folderview);
490 #endif
491         g_signal_connect(G_OBJECT(ctree), "drag_data_get",
492                          G_CALLBACK(folderview_drag_data_get),
493                          folderview);
494
495         g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
496                                G_CALLBACK(folderview_tree_expanded),
497                                folderview);
498         g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
499                                G_CALLBACK(folderview_tree_collapsed),
500                                folderview);
501
502         g_signal_connect(G_OBJECT(ctree), "resize_column",
503                          G_CALLBACK(folderview_col_resized),
504                          folderview);
505
506         /* drop callback */
507         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
508                           folderview_drag_types, 2,
509                           GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
510         g_signal_connect(G_OBJECT(ctree), "drag_motion",
511                          G_CALLBACK(folderview_drag_motion_cb),
512                          folderview);
513         g_signal_connect(G_OBJECT(ctree), "drag_leave",
514                          G_CALLBACK(folderview_drag_leave_cb),
515                          folderview);
516         g_signal_connect(G_OBJECT(ctree), "drag_data_received",
517                          G_CALLBACK(folderview_drag_received_cb),
518                          folderview);
519         g_signal_connect(G_OBJECT(ctree), "drag_end",
520                          G_CALLBACK(folderview_drag_end_cb),
521                          folderview);
522
523         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
524
525         return ctree;
526 }
527
528 void folderview_set_column_order(FolderView *folderview)
529 {
530         GtkWidget *ctree = folderview->ctree;
531         FolderItem *item = folderview_get_selected_item(folderview);
532         FolderItem *sel_item = NULL, *op_item = NULL;
533         GtkWidget *scrolledwin = folderview->scrolledwin;
534
535         if (folderview->drag_timer_id != 0) {
536                 g_source_remove(folderview->drag_timer_id);
537                 folderview->drag_timer_id = 0;
538         }
539         if (folderview->deferred_refresh_id != 0) {
540                 g_source_remove(folderview->deferred_refresh_id);
541                 folderview->deferred_refresh_id = 0;
542         }
543         if (folderview->scroll_timeout_id != 0) {
544                 g_source_remove(folderview->scroll_timeout_id);
545                 folderview->scroll_timeout_id = 0;
546         }
547         if (folderview->postpone_select_id != 0) {
548                 g_source_remove(folderview->postpone_select_id);
549                 folderview->postpone_select_id = 0;
550         }
551
552         if (folderview->selected)
553                 sel_item = folderview_get_selected_item(folderview);
554         if (folderview->opened)
555                 op_item = folderview_get_opened_item(folderview);
556
557         debug_print("recreating tree...\n");
558         gtk_widget_destroy(folderview->ctree);
559
560
561         folderview->ctree = ctree = folderview_ctree_create(folderview);
562         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
563                                             GTK_CMCLIST(ctree)->hadjustment);
564         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
565                                             GTK_CMCLIST(ctree)->vadjustment);
566         gtk_widget_show(ctree);
567         
568         if (sel_item)
569                 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
570         if (op_item)
571                 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
572
573         folderview_set(folderview);
574         folderview_column_set_titles(folderview);
575
576         folderview_select(folderview,item);
577 }
578
579 FolderView *folderview_create(void)
580 {
581         FolderView *folderview;
582         GtkWidget *scrolledwin;
583         GtkWidget *ctree;
584
585         debug_print("Creating folder view...\n");
586         folderview = g_new0(FolderView, 1);
587
588         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
589         gtk_scrolled_window_set_policy
590                 (GTK_SCROLLED_WINDOW(scrolledwin),
591                  GTK_POLICY_AUTOMATIC,
592                  prefs_common.folderview_vscrollbar_policy);
593         gtk_widget_set_size_request(scrolledwin,
594                              prefs_common.folderview_width,
595                              prefs_common.folderview_height);
596
597         folderview->scrolledwin  = scrolledwin;
598         ctree = folderview_ctree_create(folderview);
599         
600         /* create popup factories */
601         folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
602         g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
603
604         folderview->ctree        = ctree;
605
606         folderview->folder_update_callback_id =
607                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
608         folderview->folder_item_update_callback_id =
609                 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
610
611         gtk_widget_show_all(scrolledwin);
612         
613         folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
614         folderview_list = g_list_append(folderview_list, folderview);
615
616         folderview->drag_timer_id       = 0;
617         folderview->deferred_refresh_id = 0;
618         folderview->scroll_timeout_id   = 0;
619         folderview->postpone_select_id  = 0;
620
621         return folderview;
622 }
623
624 void folderview_init(FolderView *folderview)
625 {
626         GtkWidget *ctree = folderview->ctree;
627         GdkColor gdk_color;
628         PangoFontDescription *normal_font;
629
630         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
631         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
632         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
633         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
634         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
635         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
636         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
637         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
638         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
639         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
640         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
641         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
642         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
643         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
644         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
645         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
646         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
647         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
648         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
649         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
650         stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
651         stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
652         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
653
654         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
655         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
656         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
657         stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
658         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
659         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
660         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
661         stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
662         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
663         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
664         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
665         stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
666         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
667         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
668         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
669         stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
670         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
671         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
672         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
673         stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
674         stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
675         stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
676
677         normal_font = pango_font_description_from_string(NORMAL_FONT);
678         if (normal_font) {
679                 gtk_widget_modify_font(ctree, normal_font);
680                 pango_font_description_free(normal_font);
681         }
682         gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
683
684         if (!normal_style) {
685                 PangoFontDescription *font_desc;
686                 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
687                 font_desc = pango_font_description_from_string(NORMAL_FONT);
688                 if (font_desc) {
689                         if (normal_style->font_desc)
690                                 pango_font_description_free
691                                         (normal_style->font_desc);
692                         normal_style->font_desc = font_desc;
693                 }
694                 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
695                 normal_color_style = gtk_style_copy(normal_style);
696                 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
697         }
698
699         if (!bold_style) {
700                 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
701                 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
702                 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
703                         PangoFontDescription *font_desc;
704                         font_desc = pango_font_description_from_string(NORMAL_FONT);
705                         if (font_desc) {
706                                 pango_font_description_free(bold_style->font_desc);
707                                 bold_style->font_desc = font_desc;
708                         }
709                         pango_font_description_set_weight
710                                 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
711                 } else {
712                         PangoFontDescription *font_desc;
713                         font_desc = pango_font_description_from_string(BOLD_FONT);
714                         if (font_desc) {
715                                 if (bold_style->font_desc)
716                                         pango_font_description_free
717                                                 (bold_style->font_desc);
718                                 bold_style->font_desc = font_desc;
719                         }
720                 }
721                 bold_color_style = gtk_style_copy(bold_style);
722                 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
723
724                 bold_tgtfold_style = gtk_style_copy(bold_style);
725                 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
726         }
727 }
728
729 static gboolean folderview_defer_set(gpointer data)
730 {
731         FolderView *folderview = (FolderView *)data;
732         MainWindow *mainwin = folderview->mainwin;
733         
734         if (!mainwin)
735                 return FALSE;
736         if (mainwin->lock_count)
737                 return TRUE;
738                 
739         debug_print("doing deferred folderview_set now\n");
740         folderview_set(folderview);
741
742         folderview->deferred_refresh_id = 0;
743         return FALSE;
744 }
745
746 void folderview_set(FolderView *folderview)
747 {
748         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
749         MainWindow *mainwin = folderview->mainwin;
750         FolderItem *sel_item = NULL, *op_item = NULL;
751
752         if (!mainwin)
753                 return;
754
755         if (mainwin->lock_count) {
756                 if (folderview->deferred_refresh_id == 0)
757                         folderview->deferred_refresh_id = 
758                                 g_timeout_add(500, folderview_defer_set, folderview);
759                 debug_print("deferred folderview_set\n");
760                 return;
761         }
762
763         inc_lock();
764         debug_print("Setting folder info...\n");
765         STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
766
767         main_window_cursor_wait(mainwin);
768
769         if (folderview->selected)
770                 sel_item = folderview_get_selected_item(folderview);
771         if (folderview->opened)
772                 op_item = folderview_get_opened_item(folderview);
773
774         folderview->selected = NULL;
775         folderview->opened = NULL;
776
777         gtk_cmclist_freeze(GTK_CMCLIST(ctree));
778         gtk_cmclist_clear(GTK_CMCLIST(ctree));
779
780         folderview_set_folders(folderview);
781
782         if (sel_item)
783                 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
784         if (op_item)
785                 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
786
787         gtk_cmclist_thaw(GTK_CMCLIST(ctree));
788         main_window_cursor_normal(mainwin);
789         STATUSBAR_POP(mainwin);
790         inc_unlock();
791 }
792
793 void folderview_set_all(void)
794 {
795         GList *list;
796
797         for (list = folderview_list; list != NULL; list = list->next)
798                 folderview_set((FolderView *)list->data);
799 }
800
801 void folderview_select(FolderView *folderview, FolderItem *item)
802 {
803         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
804         GtkCMCTreeNode *node;
805         GtkCMCTreeNode *old_selected = folderview->selected;
806
807         if (!item) return;
808
809         node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
810         if (node) folderview_select_node(folderview, node);
811
812         if (old_selected != node)
813                 folder_update_op_count();
814 }
815
816 static void mark_all_read_cb(GtkAction *action, gpointer data)
817 {
818         mark_all_read_handler(action, data, FALSE);
819 }
820
821 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
822 {
823         mark_all_read_handler(action, data, TRUE);
824 }
825
826 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
827 {
828         FolderView *folderview = (FolderView *)data;
829         FolderItem *item;
830         AlertValue val;
831         gchar *message;
832         
833         item = folderview_get_selected_item(folderview);
834         if (item == NULL)
835                 return;
836
837         message = recursive? _("Do you really want to mark all mails in this "
838                                "folder and its sub-folders as read?") :
839                              _("Do you really want to mark all mails in this "
840                                "folder as read?");
841         if (folderview->summaryview->folder_item != item &&
842             prefs_common.ask_mark_all_read) {
843                 val = alertpanel_full(_("Mark all as read"),
844                           message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
845                           TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
846
847                 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
848                         return;
849                 else if (val & G_ALERTDISABLE)
850                         prefs_common.ask_mark_all_read = FALSE;
851         }
852         
853         folder_item_update_freeze();
854         if (folderview->summaryview->folder_item != item && !recursive)
855                 summary_lock(folderview->summaryview);
856         else
857                 summary_freeze(folderview->summaryview);
858                 
859         if (recursive)
860                 folderutils_mark_all_read_recursive(item);
861         else
862                 folderutils_mark_all_read(item);
863         
864         if (folderview->summaryview->folder_item != item && !recursive)
865                 summary_unlock(folderview->summaryview);
866         else
867                 summary_thaw(folderview->summaryview);
868         folder_item_update_thaw();
869 }
870
871 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
872 {
873         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
874
875         cm_return_if_fail(node != NULL);
876
877         if (folderview->open_folder) {
878                 return;
879         }
880
881         gtk_cmclist_freeze(GTK_CMCLIST(ctree));
882         gtkut_ctree_expand_parent_all(ctree, node);
883
884         folderview->open_folder = TRUE;
885         gtkut_ctree_set_focus_row(ctree, node);
886         gtk_cmctree_select(ctree, node);
887         gtk_cmclist_thaw(GTK_CMCLIST(ctree));
888         if ((folderview->summaryview->folder_item &&
889             folderview->summaryview->folder_item->total_msgs > 0) ||
890              prefs_common.layout_mode == SMALL_LAYOUT)
891                 summary_grab_focus(folderview->summaryview);
892         else
893                 gtk_widget_grab_focus(folderview->ctree);
894 }
895
896 void folderview_unselect(FolderView *folderview)
897 {
898         if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
899                 gtk_cmctree_collapse
900                         (GTK_CMCTREE(folderview->ctree), folderview->opened);
901
902         folderview->selected = folderview->opened = NULL;
903 }
904
905 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
906                                                       GtkCMCTreeNode *node,
907                                                       MsgPermFlags flag)
908 {
909         FolderItem *item;
910
911         if (node)
912                 node = gtkut_ctree_node_next(ctree, node);
913         else
914                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
915
916         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
917                 item = gtk_cmctree_node_get_row_data(ctree, node);
918                 if (!item)
919                         continue;
920                 if (item->stype == F_TRASH || item->stype == F_DRAFT)
921                         continue;
922                 switch (flag) {
923                 case MSG_UNREAD:
924                         if(item->unread_msgs > 0)
925                                 return node;
926                         break;
927                 case MSG_NEW:
928                         if(item->new_msgs > 0)
929                                 return node;
930                         break;
931                 case MSG_MARKED:
932                         if(item->marked_msgs > 0)
933                                 return node;
934                         break;
935                 }
936         }
937
938         return NULL;
939 }
940
941 void folderview_select_next_with_flag(FolderView *folderview,
942                                       MsgPermFlags flag,
943                                       gboolean force_open)
944 {
945         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
946         GtkCMCTreeNode *node = NULL;
947         EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
948         gboolean last_open = prefs_common.always_show_msg;
949         
950         switch (flag) {
951         case MSG_UNREAD:
952                 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
953                 break;
954         case MSG_NEW:
955                 prefs_common.summary_select_prio[0] = ACTION_NEW;
956                 break;
957         case MSG_MARKED:
958                 prefs_common.summary_select_prio[0] = ACTION_MARKED;
959                 break;
960         }
961         prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
962
963         node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
964         if (node != NULL) {
965                 folderview_select_node(folderview, node);
966                 goto out;
967         }
968
969         if (!folderview->opened ||
970             folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
971                 goto out;
972         }
973
974         /* search again from the first node */
975         node = folderview_find_next_with_flag(ctree, NULL, flag);
976         if (node != NULL)
977                 folderview_select_node(folderview, node);
978
979 out:
980         prefs_common.summary_select_prio[0] = last_summary_select_prio;
981         prefs_common.always_show_msg = last_open;
982 }
983
984 FolderItem *folderview_get_selected_item(FolderView *folderview)
985 {
986         g_return_val_if_fail(folderview != NULL, NULL);
987         g_return_val_if_fail(folderview->ctree != NULL, NULL);
988
989         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
990
991         if (!folderview->selected) return NULL;
992         return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
993 }
994
995 FolderItem *folderview_get_opened_item(FolderView *folderview)
996 {
997         g_return_val_if_fail(folderview != NULL, NULL);
998         g_return_val_if_fail(folderview->ctree != NULL, NULL);
999
1000         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1001
1002         if (!folderview->opened) return NULL;
1003         return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1004 }
1005
1006 static void folderview_set_folders(FolderView *folderview)
1007 {
1008         GList *list;
1009         list = folder_get_list();
1010
1011         for (; list != NULL; list = list->next) {
1012                 folderview_append_folder(folderview, FOLDER(list->data));
1013         }
1014 }
1015
1016 static gchar *get_scan_str(FolderItem *item)
1017 {
1018         if (item->path)
1019                 return g_strdup_printf(_("Scanning folder %s/%s..."),
1020                                       item->folder->name, item->path);
1021         else
1022                 return g_strdup_printf(_("Scanning folder %s..."),
1023                                       item->folder->name);      
1024 }
1025 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1026                                       gpointer data)
1027 {
1028         GList *list;
1029         for (list = folderview_list; list != NULL; list = list->next) {
1030                 FolderView *folderview = (FolderView *)list->data;
1031                 MainWindow *mainwin = folderview->mainwin;
1032                 gchar *str = get_scan_str(item);
1033
1034                 STATUSBAR_PUSH(mainwin, str);
1035                 STATUSBAR_POP(mainwin);
1036                 g_free(str);
1037         }
1038 }
1039
1040 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1041 {
1042         GtkWidget *window;
1043         MainWindow *mainwin = mainwindow_get_mainwindow();
1044         FolderView *folderview = NULL;
1045         GtkAdjustment *pos = NULL;
1046         gint height = 0;
1047
1048         cm_return_if_fail(folder != NULL);
1049
1050         if (!folder->klass->scan_tree) return;
1051
1052         if (rebuild && 
1053             alertpanel_full(_("Rebuild folder tree"), 
1054                          _("Rebuilding the folder tree will remove "
1055                            "local caches. Do you want to continue?"),
1056                          GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1057                          NULL, ALERT_WARNING, G_ALERTDEFAULT) 
1058                 != G_ALERTALTERNATE) {
1059                 return;
1060         }
1061
1062         inc_lock();
1063         if (rebuild)
1064                 window = label_window_create(_("Rebuilding folder tree..."));
1065         else 
1066                 window = label_window_create(_("Scanning folder tree..."));
1067
1068         if (mainwin)
1069                 folderview = mainwin->folderview;
1070         
1071         if (folderview) {
1072                 pos = gtk_scrolled_window_get_vadjustment(
1073                                         GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1074                 height = gtk_adjustment_get_value(pos);
1075         }
1076
1077         folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1078         folder_scan_tree(folder, rebuild);
1079         folder_set_ui_func(folder, NULL, NULL);
1080
1081         folderview_set_all();
1082
1083         if (folderview) {
1084                 pos = gtk_scrolled_window_get_vadjustment(
1085                                         GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1086                 gtk_adjustment_set_value(pos, height);
1087                 gtk_adjustment_changed(pos);
1088         }
1089         label_window_destroy(window);
1090         inc_unlock();
1091 }
1092
1093 /** folderview_check_new()
1094  *  Scan and update the folder and return the 
1095  *  count the number of new messages since last check. 
1096  *  \param folder the folder to check for new messages
1097  *  \return the number of new messages since last check
1098  */
1099 gint folderview_check_new(Folder *folder)
1100 {
1101         GList *list;
1102         FolderItem *item;
1103         FolderView *folderview;
1104         GtkCMCTree *ctree;
1105         GtkCMCTreeNode *node;
1106         gint new_msgs = 0;
1107         gint former_new_msgs = 0;
1108         gint former_new = 0, former_unread = 0, former_total;
1109
1110         for (list = folderview_list; list != NULL; list = list->next) {
1111                 folderview = (FolderView *)list->data;
1112                 ctree = GTK_CMCTREE(folderview->ctree);
1113                 folderview->scanning_folder = folder;
1114                 inc_lock();
1115                 main_window_lock(folderview->mainwin);
1116
1117                 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1118                      node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1119                         gchar *str = NULL;
1120                         item = gtk_cmctree_node_get_row_data(ctree, node);
1121                         if (!item || !item->path || !item->folder) continue;
1122                         if (item->no_select) continue;
1123                         if (folder && folder != item->folder) continue;
1124                         if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1125                         if (!item->prefs->newmailcheck) continue;
1126                         if (item->processing_pending == TRUE) {
1127                                 debug_print("skipping %s, processing pending\n",
1128                                         item->path ? item->path : item->name);
1129                                 continue;
1130                         }
1131                         if (item->scanning != ITEM_NOT_SCANNING) {
1132                                 debug_print("skipping %s, scanning\n",
1133                                         item->path ? item->path : item->name);
1134                                 continue;
1135                         }
1136
1137                         str = get_scan_str(item);
1138
1139                         STATUSBAR_PUSH(folderview->mainwin, str);
1140                         GTK_EVENTS_FLUSH();
1141                         g_free(str);
1142
1143                         folderview_scan_tree_func(item->folder, item, NULL);
1144                         former_new    = item->new_msgs;
1145                         former_unread = item->unread_msgs;
1146                         former_total  = item->total_msgs;
1147
1148                         if (item->folder->klass->scan_required &&
1149                             (item->folder->klass->scan_required(item->folder, item) ||
1150                              item->folder->inbox == item ||
1151                              item->opened == TRUE ||
1152                              item->processing_pending == TRUE)) {
1153                                 if (folder_item_scan(item) < 0) {
1154                                         if (folder) {
1155                                                 summaryview_unlock(folderview->summaryview, item);
1156                                                 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1157                                                         log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1158                                                                 item->path ? item->path:item->name);
1159                                                         STATUSBAR_POP(folderview->mainwin);
1160                                                         continue;
1161                                                 } else if (!FOLDER_IS_LOCAL(folder)) {
1162                                                         STATUSBAR_POP(folderview->mainwin);
1163                                                         break;
1164                                                 }
1165                                         }
1166                                 }
1167                         } else if (!item->folder->klass->scan_required) {
1168                                 if (folder_item_scan(item) < 0) {
1169                                         summaryview_unlock(folderview->summaryview, item);
1170                                         if (folder && !FOLDER_IS_LOCAL(folder)) {
1171                                                 STATUSBAR_POP(folderview->mainwin);
1172                                                 break;
1173                                         }
1174                                 }
1175                         }
1176                         if (former_new    != item->new_msgs ||
1177                             former_unread != item->unread_msgs ||
1178                             former_total  != item->total_msgs)
1179                                 folderview_update_node(folderview, node);
1180
1181                         new_msgs += item->new_msgs;
1182                         former_new_msgs += former_new;
1183                         STATUSBAR_POP(folderview->mainwin);
1184                 }
1185                 folderview->scanning_folder = NULL;
1186                 main_window_unlock(folderview->mainwin);
1187                 inc_unlock();
1188         }
1189
1190         folder_write_list();
1191         /* Number of new messages since last check is the just the difference 
1192          * between former_new_msgs and new_msgs. If new_msgs is less than
1193          * former_new_msgs, that would mean another session accessed the folder
1194          * and the result is not well defined.
1195          */
1196         new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1197         return new_msgs;
1198 }
1199
1200 void folderview_check_new_all(void)
1201 {
1202         GList *list;
1203         GtkWidget *window;
1204         FolderView *folderview;
1205
1206         folderview = (FolderView *)folderview_list->data;
1207
1208         inc_lock();
1209         main_window_lock(folderview->mainwin);
1210         window = label_window_create
1211                 (_("Checking for new messages in all folders..."));
1212
1213         list = folder_get_list();
1214         for (; list != NULL; list = list->next) {
1215                 Folder *folder = list->data;
1216
1217                 folderview_check_new(folder);
1218         }
1219
1220         folder_write_list();
1221         folderview_set_all();
1222
1223         label_window_destroy(window);
1224         main_window_unlock(folderview->mainwin);
1225         inc_unlock();
1226 }
1227
1228 static gboolean folderview_have_children_sub(FolderView *folderview,
1229                                              FolderItem *item,
1230                                              gboolean in_sub)
1231 {
1232         GNode *node = NULL;
1233         
1234         if (!item || !item->folder || !item->folder->node)
1235                 return FALSE;
1236                 
1237         node = item->folder->node;
1238         
1239         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1240         node = node->children;
1241
1242         if (in_sub && item->total_msgs > 0) {
1243                 return TRUE;
1244         }
1245
1246         while (node != NULL) {
1247                 if (node && node->data) {
1248                         FolderItem *next_item = (FolderItem*) node->data;
1249                         node = node->next;
1250                         if (folderview_have_children_sub(folderview, 
1251                                                          next_item, TRUE))
1252                                 return TRUE;
1253                 }
1254         }
1255
1256         return FALSE;
1257 }
1258
1259 static gboolean folderview_have_children(FolderView *folderview,
1260                                          FolderItem *item)
1261 {
1262         return folderview_have_children_sub(folderview, item, FALSE);
1263 }
1264
1265 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1266                                                  FolderItem *item,
1267                                                  gboolean in_sub)
1268 {
1269         GNode *node = NULL;
1270         
1271         if (!item || !item->folder || !item->folder->node)
1272                 return FALSE;
1273                 
1274         node = item->folder->node;
1275         
1276         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1277         node = node->children;
1278
1279         if (in_sub &&
1280             (item->new_msgs > 0 ||
1281             (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1282                 return TRUE;
1283         }
1284
1285         while (node != NULL) {
1286                 if (node && node->data) {
1287                         FolderItem *next_item = (FolderItem*) node->data;
1288                         node = node->next;
1289                         if (folderview_have_new_children_sub(folderview, 
1290                                                              next_item, TRUE))
1291                                 return TRUE;
1292                 }
1293         }
1294
1295         return FALSE;
1296 }
1297
1298 static gboolean folderview_have_new_children(FolderView *folderview,
1299                                              FolderItem *item)
1300 {
1301         return folderview_have_new_children_sub(folderview, item, FALSE);
1302 }
1303
1304 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1305                                                     FolderItem *item, 
1306                                                     gboolean in_sub)
1307 {
1308         GNode *node = NULL;
1309         
1310         if (!item || !item->folder || !item->folder->node)
1311                 return FALSE;
1312         
1313         node = item->folder->node;
1314         
1315         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1316         node = node->children;
1317
1318         if (in_sub &&
1319             (item->unread_msgs > 0 ||
1320             (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1321                 return TRUE;
1322         }
1323
1324         while (node != NULL) {
1325                 if (node && node->data) {
1326                         FolderItem *next_item = (FolderItem*) node->data;
1327                         node = node->next;
1328                         if (folderview_have_unread_children_sub(folderview, 
1329                                                                 next_item, 
1330                                                                 TRUE))
1331                                 return TRUE;
1332                 }
1333         }
1334
1335         return FALSE;
1336 }
1337
1338 static gboolean folderview_have_unread_children(FolderView *folderview,
1339                                                 FolderItem *item)
1340 {
1341         return folderview_have_unread_children_sub(folderview, item, FALSE);
1342 }
1343
1344 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1345                                                       FolderItem *item,
1346                                                       gboolean in_sub)
1347 {
1348         GNode *node = NULL;
1349
1350         if (!item || !item->folder || !item->folder->node)
1351                 return FALSE;
1352
1353         node = item->folder->node;
1354         
1355         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1356         node = node->children;
1357
1358         if (in_sub && item->search_match){
1359                 return TRUE;
1360         }
1361
1362         while (node != NULL) {
1363                 if (node && node->data) {
1364                         FolderItem *next_item = (FolderItem*) node->data;
1365                         node = node->next;
1366                         if (folderview_have_matching_children_sub(folderview, 
1367                                                                   next_item, 
1368                                                                   TRUE))
1369                                 return TRUE;
1370                 }
1371         }
1372
1373         return FALSE;
1374 }
1375
1376 static gboolean folderview_have_matching_children(FolderView *folderview,
1377                                                   FolderItem *item)
1378 {
1379         return folderview_have_matching_children_sub(folderview, item, FALSE);
1380 }
1381
1382 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1383                                                     FolderItem *item,
1384                                                     gboolean in_sub)
1385 {
1386         GNode *node = NULL;
1387         
1388         if (!item || !item->folder || !item->folder->node)
1389                 return FALSE;
1390                 
1391         node = item->folder->node;
1392         
1393         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1394         node = node->children;
1395
1396         if (item->marked_msgs != 0) {
1397                 return TRUE;
1398         }
1399
1400         while (node != NULL) {
1401                 if (node && node->data) {
1402                         FolderItem *next_item = (FolderItem*) node->data;
1403                         node = node->next;
1404                         if (folderview_have_marked_children_sub(folderview,
1405                                                                 next_item, TRUE))
1406                                 return TRUE;
1407                 }
1408         }
1409
1410         return FALSE;
1411 }
1412
1413 static gboolean folderview_have_marked_children(FolderView *folderview,
1414                                              FolderItem *item)
1415 {
1416         return folderview_have_marked_children_sub(folderview, item, FALSE);
1417 }
1418
1419 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1420 {
1421         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1422         GtkStyle *style = NULL;
1423         GtkStyle *color_style = NULL;
1424         FolderItem *item;
1425         GdkPixbuf *xpm, *openxpm;
1426         static GdkPixbuf *searchicon;
1427         gboolean mark = FALSE;
1428         gchar *name;
1429         gchar *str;
1430         gboolean add_unread_mark;
1431         gboolean add_sub_match_mark;
1432         gboolean use_bold, use_color;
1433         gint *col_pos = folderview->col_pos;
1434         SpecialFolderItemType stype;
1435         
1436         item = gtk_cmctree_node_get_row_data(ctree, node);
1437         cm_return_if_fail(item != NULL);
1438
1439         if (!GTK_CMCTREE_ROW(node)->expanded)
1440                 mark = folderview_have_marked_children(folderview, item);
1441         else
1442                 mark = (item->marked_msgs != 0);
1443
1444         stype = item->stype;
1445         if (stype == F_NORMAL) {
1446                 if (folder_has_parent_of_type(item, F_TRASH))
1447                         stype = F_TRASH;
1448                 else if (folder_has_parent_of_type(item, F_DRAFT))
1449                         stype = F_DRAFT;
1450                 else if (folder_has_parent_of_type(item, F_OUTBOX))
1451                         stype = F_OUTBOX;
1452                 else if (folder_has_parent_of_type(item, F_QUEUE))
1453                         stype = F_QUEUE;
1454         }
1455         switch (stype) {
1456         case F_INBOX:
1457                 if (item->hide_read_msgs || item->hide_read_threads) {
1458                         xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1459                         openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1460                 } else {
1461                         xpm = mark?m_inboxxpm:inboxxpm;
1462                         openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1463                 }
1464                 break;
1465         case F_OUTBOX:
1466                 if (item->hide_read_msgs || item->hide_read_threads) {
1467                         xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1468                         openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1469                 } else {
1470                         xpm = mark?m_outboxxpm:outboxxpm;
1471                         openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1472                 }
1473                 break;
1474         case F_QUEUE:
1475                 if (item->hide_read_msgs || item->hide_read_threads) {
1476                         xpm = mark?m_queuehrmxpm:queuehrmxpm;
1477                         openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1478                 } else {
1479                         xpm = mark?m_queuexpm:queuexpm;
1480                         openxpm = mark?m_queueopenxpm:queueopenxpm;
1481                 }
1482                 break;
1483         case F_TRASH:
1484                 if (item->hide_read_msgs || item->hide_read_threads) {
1485                         xpm = mark?m_trashhrmxpm:trashhrmxpm;
1486                         openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1487                 } else {
1488                         xpm = mark?m_trashxpm:trashxpm;
1489                         openxpm = mark?m_trashopenxpm:trashopenxpm;
1490                 }
1491                 break;
1492         case F_DRAFT:
1493                 xpm = mark?m_draftsxpm:draftsxpm;
1494                 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1495                 break;
1496         default:
1497                 if (item->hide_read_msgs || item->hide_read_threads) {
1498                         xpm = mark?m_folderhrmxpm:folderhrmxpm;
1499                         openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1500                 } else {
1501                         xpm = mark?m_folderxpm:folderxpm;
1502                         openxpm = mark?m_folderopenxpm:folderopenxpm;
1503                 }
1504         }
1505         
1506         if (item->no_select) {
1507                 xpm = openxpm = noselectxpm;
1508         }
1509
1510         name = folder_item_get_name(item);
1511
1512         if (!GTK_CMCTREE_ROW(node)->expanded) {
1513                 add_unread_mark = folderview_have_unread_children(
1514                                         folderview, item);
1515                 add_sub_match_mark = folderview_have_matching_children(
1516                                         folderview, item);
1517         } else {
1518                 add_unread_mark = FALSE;
1519                 add_sub_match_mark = FALSE;
1520         }
1521
1522         if (item->search_match) {
1523                 if (!searchicon) {
1524                         stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1525                          &searchicon);
1526                 }
1527                 xpm = openxpm = searchicon;
1528         }
1529
1530         str = NULL;
1531         if (prefs_common.display_folder_unread) {
1532                 if (folder_has_parent_of_type(item, F_QUEUE)) {
1533                         /* only total_msgs matters here */
1534                         if (item->total_msgs > 0) {
1535                                 /* show total number (should be equal to the unread number)
1536                                    and signs if any */
1537                                 str = g_strdup_printf("%s (%d%s%s)",
1538                                                         name, item->total_msgs,
1539                                                         (add_unread_mark || add_sub_match_mark) ? "+" : "", 
1540                                                         (item->unreadmarked_msgs > 0) ? "!" : "");
1541                         }
1542                 } else {
1543                         if (prefs_common.display_folder_unread == 1) {
1544                                 if (item->unread_msgs > 0) {
1545                                         /* show unread number and signs */
1546                                         str = g_strdup_printf("%s (%d%s%s)",
1547                                                                 name, item->unread_msgs,
1548                                                                 (add_unread_mark || add_sub_match_mark) ? "+" : "", 
1549                                                                 (item->unreadmarked_msgs > 0) ? "!" : "");
1550                                 }
1551                         } else {
1552                                 if (item->total_msgs > 0) {
1553                                         /* show unread number, total number and signs if any */
1554                                         str = g_strdup_printf("%s (%d/%d%s%s)",
1555                                                                 name, item->unread_msgs, item->total_msgs,
1556                                                                 (add_unread_mark || add_sub_match_mark) ? "+" : "", 
1557                                                                 (item->unreadmarked_msgs > 0) ? "!" : "");
1558                                 }
1559                         }
1560                 }
1561                 if ((str == NULL) &&
1562                         (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1563                         /* no unread/total numbers, but at least one sign */
1564                         str = g_strdup_printf("%s (%s%s)",
1565                                                 name,
1566                                                 (add_unread_mark || add_sub_match_mark) ? "+" : "", 
1567                                                 (item->unreadmarked_msgs > 0) ? "!" : "");
1568                 }
1569         }
1570         if (str == NULL) {
1571                 /* last fallback, folder name only or with +! sign */
1572                 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1573                         str = g_strdup_printf("%s%s",
1574                                                 name, " (+!)");
1575                 } else if (item->unreadmarked_msgs > 0) {
1576                         str = g_strdup_printf("%s%s",
1577                                                 name, " (!)");
1578                 } else if (add_sub_match_mark) {
1579                         str = g_strdup_printf("%s%s",
1580                                                 name, " (+)");
1581                 } else {
1582                         str = g_strdup_printf("%s", name);
1583                 }
1584         }
1585         gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1586                                 xpm, openxpm, 
1587                                 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1588         g_free(str);
1589         g_free(name);
1590
1591         if (!folder_item_parent(item)) {
1592                 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW],    "-");
1593                 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1594                 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL],  "-");
1595         } else {
1596                 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW],    item->new_msgs    > 0 ? itos(item->new_msgs)    : prefs_common.zero_replacement);
1597                 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1598                 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL],  item->total_msgs  > 0 ? itos(item->total_msgs)  : prefs_common.zero_replacement);
1599         }
1600
1601         if (folder_has_parent_of_type(item, F_OUTBOX) ||
1602             folder_has_parent_of_type(item, F_TRASH)) {
1603                 use_bold = use_color = FALSE;
1604         } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1605                 GSList *list = folder_item_get_msg_list(item);
1606                 GSList *cur;
1607                 use_bold = use_color = FALSE;
1608                 for (cur = list; cur; cur = cur->next) {
1609                         MsgInfo *msginfo = (MsgInfo *)cur->data;
1610                         if (!MSG_IS_DELETED(msginfo->flags)) {
1611                                 /* highlight queue folder if there are any messages */
1612                                 use_bold = use_color = TRUE;
1613                                 break;
1614                         }
1615                 }
1616                 if (!GTK_CMCTREE_ROW(node)->expanded &&
1617                     use_bold == FALSE &&
1618                     folderview_have_children(folderview, item))
1619                         use_bold = use_color = TRUE;
1620                 procmsg_msg_list_free(list);
1621         } else {
1622                 /* if unread messages exist, print with bold font */
1623                 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0) 
1624                                 || add_unread_mark;
1625                 /* if new messages exist, print with colored letter */
1626                 use_color =
1627                         (item->new_msgs > 0) ||
1628                         (add_unread_mark &&
1629                          folderview_have_new_children(folderview, item));       
1630         }
1631
1632         gtk_cmctree_node_set_foreground(ctree, node, NULL);
1633
1634         if (use_bold) {
1635                 GdkColor gdk_color;
1636
1637                 if (item->prefs->color > 0 && !use_color) {
1638                         gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1639                         color_style = gtk_style_copy(bold_style);
1640                         color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1641                         style = color_style;
1642                 } else if (use_color) {
1643                         style = bold_color_style;
1644                 } else
1645                         style = bold_style;
1646                 if (item->op_count > 0) {
1647                         style = bold_tgtfold_style;
1648                 }
1649         } else if (use_color) {
1650                 style = normal_color_style;
1651                 gtk_cmctree_node_set_foreground(ctree, node,
1652                                               &folderview->color_new);
1653         } else if (item->op_count > 0) {
1654                 style = bold_tgtfold_style;
1655         } else if (item->prefs->color > 0) {
1656                 GdkColor gdk_color;
1657                 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1658                 color_style = gtk_style_copy(normal_style);
1659                 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1660                 style = color_style;
1661         } else {
1662                 style = normal_style;
1663         }
1664
1665         gtk_cmctree_node_set_row_style(ctree, node, style);
1666
1667         if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1668                 folderview_update_node(folderview, node);
1669 }
1670
1671 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1672 {
1673         GList *list;
1674         FolderView *folderview;
1675         GtkCMCTree *ctree;
1676         GtkCMCTreeNode *node;
1677
1678         cm_return_if_fail(item != NULL);
1679
1680         for (list = folderview_list; list != NULL; list = list->next) {
1681                 folderview = (FolderView *)list->data;
1682                 ctree = GTK_CMCTREE(folderview->ctree);
1683
1684                 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1685                 if (node && item->search_match != matches) {
1686                         item->search_match = matches;
1687                         folderview_update_node(folderview, node);
1688                 }
1689         }
1690 }
1691
1692 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1693 {
1694         FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1695         FolderView *folderview = (FolderView *)data;
1696         GtkCMCTree *ctree;
1697         GtkCMCTreeNode *node;
1698         cm_return_val_if_fail(update_info != NULL, TRUE);
1699         cm_return_val_if_fail(update_info->item != NULL, TRUE);
1700         cm_return_val_if_fail(folderview != NULL, FALSE);
1701
1702         ctree = GTK_CMCTREE(folderview->ctree);
1703
1704         node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1705
1706         if (node) {
1707                 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1708                         folderview_update_node(folderview, node);
1709
1710                 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) && 
1711                      update_info->item == folderview->summaryview->folder_item &&
1712                      update_info->item != NULL)
1713                         if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1714                                 summary_show(folderview->summaryview, update_info->item);
1715         }
1716         
1717         return FALSE;
1718 }
1719
1720 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1721                                       GNode *gnode, GtkCMCTreeNode *cnode,
1722                                       gpointer data)
1723 {
1724         FolderView *folderview = (FolderView *)data;
1725         FolderItem *item = FOLDER_ITEM(gnode->data);
1726
1727         cm_return_val_if_fail(item != NULL, FALSE);
1728
1729         gtk_cmctree_node_set_row_data(ctree, cnode, item);
1730         folderview_update_node(folderview, cnode);
1731
1732         return TRUE;
1733 }
1734
1735 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1736                                    gpointer data)
1737 {
1738         FolderView *folderview = (FolderView *)data;
1739         FolderItem *item;
1740
1741         if (GTK_CMCTREE_ROW(node)->children) {
1742                 item = gtk_cmctree_node_get_row_data(ctree, node);
1743                 cm_return_if_fail(item != NULL);
1744
1745                 if (!item->collapsed)
1746                         gtk_cmctree_expand(ctree, node);
1747                 else
1748                         folderview_update_node(folderview, node);
1749         }
1750 }
1751
1752 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1753                                GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1754 {
1755         if (item) {
1756                 GtkCMCTreeNode *node, *parent, *sibling;
1757
1758                 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1759                 if (!node)
1760                         g_warning("%s not found.", item->path);
1761                 else {
1762                         parent = GTK_CMCTREE_ROW(node)->parent;
1763                         if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1764                                 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1765                         else
1766                                 sibling = GTK_CMCTREE_ROW(parent)->children;
1767                         while (sibling) {
1768                                 FolderItem *tmp;
1769
1770                                 tmp = gtk_cmctree_node_get_row_data
1771                                         (ctree, sibling);
1772                                 if (tmp && tmp->stype != F_NORMAL)
1773                                         sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1774                                 else
1775                                         break;
1776                         }
1777                         if (node != sibling)
1778                                 gtk_cmctree_move(ctree, node, parent, sibling);
1779                 }
1780
1781                 *prev = node;
1782         }
1783 }
1784
1785 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1786                                     Folder *folder)
1787 {
1788         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1789         GtkCMCTreeNode *prev = NULL;
1790
1791         gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1792         gtk_sctree_sort_recursive(ctree, root);
1793         if (root && GTK_CMCTREE_ROW(root)->parent) {
1794                 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1795                 return;
1796         }
1797         set_special_folder(ctree, folder->inbox, root, &prev);
1798         set_special_folder(ctree, folder->outbox, root, &prev);
1799         set_special_folder(ctree, folder->draft, root, &prev);
1800         set_special_folder(ctree, folder->queue, root, &prev);
1801         set_special_folder(ctree, folder->trash, root, &prev);
1802         gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1803 }
1804
1805 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1806 {
1807         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1808         GtkCMCTreeNode *root;
1809
1810         cm_return_if_fail(folder != NULL);
1811
1812         root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1813                                       folderview_gnode_func, folderview);
1814         gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1815                                 folderview);
1816         folderview_sort_folders(folderview, root, folder);
1817 }
1818
1819 /* callback functions */
1820 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row, 
1821                                 GdkEventButton *event)
1822 {
1823         FolderItem *item;
1824         Folder *folder;
1825         FolderViewPopup *fpopup;
1826         GtkActionGroup *action_group;
1827         GtkWidget *popup;
1828         FolderItem *special_trash = NULL, *special_queue = NULL;
1829         PrefsAccount *ac;
1830         GtkUIManager *ui_manager = gtk_ui_manager_new();
1831         
1832         if (folderview->ui_manager)
1833                 g_object_unref(folderview->ui_manager);
1834
1835         folderview->ui_manager = ui_manager;
1836         item = folderview_get_selected_item(folderview);
1837
1838         cm_return_if_fail(item != NULL);
1839         cm_return_if_fail(item->folder != NULL);
1840         folder = item->folder;
1841
1842         fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1843
1844         if (fpopup != NULL)
1845                 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1846         else {
1847                 fpopup = g_hash_table_lookup(folderview_popups, "common");
1848                 action_group = g_hash_table_lookup(folderview->popups, "common");
1849         }
1850         
1851         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1852         MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1853         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1854         
1855         if (fpopup->add_menuitems)      
1856                 fpopup->add_menuitems(ui_manager, item);
1857
1858         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1859         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1860         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1861         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1862         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1863         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1864         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1865
1866         if (fpopup->set_sensitivity != NULL)
1867                 fpopup->set_sensitivity(ui_manager, item);
1868
1869         if (NULL != (ac = account_find_from_item(item))) {
1870                 special_trash = account_get_special_folder(ac, F_TRASH);
1871                 special_queue = account_get_special_folder(ac, F_QUEUE);
1872         }
1873         
1874         if ((item == folder->trash || item == special_trash
1875              || folder_has_parent_of_type(item, F_TRASH))) {
1876                 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1877                 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1878         } 
1879         
1880         if ((item == folder->queue || item == special_queue
1881              || folder_has_parent_of_type(item, F_QUEUE))) {
1882                 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1883                 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1884         } 
1885         
1886 #define SET_SENS(name, sens) \
1887         cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1888
1889         SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1890         SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1891         SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 && 
1892                  folderview->selected == folderview->opened);
1893         SET_SENS("FolderViewPopup/Properties", TRUE);
1894
1895         SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1896                  item->total_msgs >= 1 && !item->processing_pending);
1897         SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL && 
1898                 !item->no_select && !item->processing_pending);
1899
1900         if (item == folder->trash || item == special_trash
1901             || folder_has_parent_of_type(item, F_TRASH)) {
1902                 GSList *msglist = folder_item_get_msg_list(item);
1903                 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1904                 procmsg_msg_list_free(msglist);
1905         }
1906         if (item == folder->queue || item == special_queue
1907             || folder_has_parent_of_type(item, F_QUEUE)) {
1908                 GSList *msglist = folder_item_get_msg_list(item);
1909                 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1910                 procmsg_msg_list_free(msglist);
1911         }
1912 #undef SET_SENS
1913
1914         popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1915                         gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1916         g_signal_connect(G_OBJECT(popup), "selection_done",
1917                          G_CALLBACK(folderview_popup_close),
1918                          folderview);
1919         gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1920                        event->button, event->time);
1921 }
1922
1923 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1924                                           FolderView *folderview)
1925 {
1926         GtkCMCList *clist = GTK_CMCLIST(ctree);
1927         gint prev_row = -1, row = -1, column = -1;
1928
1929         if (!event) return FALSE;
1930
1931         if (event->button == 1 || event->button == 2) {
1932                 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1933                         folderview->open_folder = TRUE;
1934
1935                 if (event->type == GDK_2BUTTON_PRESS) {
1936                         if (clist->selection) {
1937                                 GtkCMCTreeNode *node;
1938
1939                                 node = GTK_CMCTREE_NODE(clist->selection->data);
1940                                 if (node) {
1941                                         gtk_cmctree_toggle_expansion(
1942                                                 GTK_CMCTREE(ctree),
1943                                                 node);
1944                                         folderview->open_folder = FALSE;
1945                                 }
1946                         }
1947                 }
1948                 return FALSE;
1949         }
1950
1951         if (event->button == 2 || event->button == 3) {
1952                 /* right clicked */
1953                 if (clist->selection) {
1954                         GtkCMCTreeNode *node;
1955
1956                         node = GTK_CMCTREE_NODE(clist->selection->data);
1957                         if (node)
1958                                 prev_row = gtkut_ctree_get_nth_from_node
1959                                         (GTK_CMCTREE(ctree), node);
1960                 }
1961
1962                 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1963                                                   &row, &column))
1964                         return FALSE;
1965                 if (prev_row != row) {
1966                         gtk_cmclist_unselect_all(clist);
1967                         if (event->button == 2)
1968                                 folderview_select_node
1969                                         (folderview,
1970                                          gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1971                                                             row));
1972                         else
1973                                 gtk_cmclist_select_row(clist, row, column);
1974                 }
1975         }
1976
1977         if (event->button != 3) return FALSE;
1978
1979         folderview_set_sens_and_popup_menu(folderview, row, event);
1980         return FALSE;
1981 }
1982
1983 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1984                                            FolderView *folderview)
1985 {
1986         int row = -1, column = -1;
1987
1988         if (!event) return FALSE;
1989
1990         if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1991                                           &row, &column))
1992                 return FALSE;
1993         if (event->button == 1 && folderview->open_folder == FALSE &&
1994             folderview->opened != NULL) {
1995                 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1996                                           folderview->opened);
1997                 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1998         }
1999
2000         return FALSE;
2001 }
2002
2003 #define BREAK_ON_MODIFIER_KEY() \
2004         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2005
2006 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2007                                        FolderView *folderview)
2008 {
2009         GtkCMCTreeNode *node;
2010         FolderItem *item;
2011
2012         if (!event) return FALSE;
2013
2014         if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2015                 return FALSE;
2016
2017         switch (event->keyval) {
2018         case GDK_KEY_Right:
2019                 if (folderview->selected) {
2020                         if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2021                                         && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2022                                 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2023                                                 folderview->selected);
2024                         else
2025                                 folderview_select_node(folderview,
2026                                                        folderview->selected);
2027                 }
2028                 break;
2029 #ifdef GENERIC_UMPC
2030         case GDK_KEY_Return:
2031                 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2032                         gtk_cmctree_toggle_expansion(
2033                                 GTK_CMCTREE(folderview->ctree),
2034                                 folderview->selected);
2035                 }
2036                 break;  
2037 #else
2038         case GDK_KEY_Return:
2039         case GDK_KEY_KP_Enter:
2040                 if (folderview->selected)
2041                         folderview_select_node(folderview, folderview->selected);
2042                 break;
2043 #endif
2044         case GDK_KEY_space:
2045                 BREAK_ON_MODIFIER_KEY();
2046                 if (folderview->selected) {
2047                         if (folderview->opened == folderview->selected &&
2048                             (!folderview->summaryview->folder_item ||
2049                              folderview->summaryview->folder_item->total_msgs == 0))
2050                                 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2051                         else
2052                                 folderview_select_node(folderview,
2053                                                        folderview->selected);
2054                 }
2055                 break;
2056         case GDK_KEY_Left:
2057                 if (folderview->selected) {
2058                         if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2059                                 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2060                                                 folderview->selected);
2061                         } else {
2062                                 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2063                                                 folderview->selected))) {
2064                                         if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2065                                                         NULL, folder_item_parent(item)))) {
2066                                                 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2067                                                 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2068                                                         gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2069                                                                         node, -1, 0, 0);
2070                                         }
2071                                 }
2072                         }
2073                 }
2074                 break;
2075         case GDK_KEY_Home:
2076         case GDK_KEY_End:
2077                 if (event->keyval == GDK_KEY_Home)
2078                         node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2079                 else
2080                         node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2081                                         gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2082
2083                 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2084
2085                 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2086                         gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2087                                         node, -1, 0, 0);
2088                 break;
2089         default:
2090                 break;
2091         }
2092
2093         return FALSE;
2094 }
2095
2096 typedef struct _PostponedSelectData
2097 {
2098         GtkCMCTree *ctree;
2099         GtkCMCTreeNode *row;
2100         gint column;
2101         FolderView *folderview;
2102 } PostponedSelectData;
2103
2104 static gboolean postpone_select(void *data)
2105 {
2106         PostponedSelectData *psdata = (PostponedSelectData *)data;
2107         debug_print("trying again\n");
2108
2109         psdata->folderview->postpone_select_id = 0;
2110         psdata->folderview->open_folder = TRUE;
2111         main_window_cursor_normal(psdata->folderview->mainwin);
2112         STATUSBAR_POP(psdata->folderview->mainwin);
2113         folderview_selected(psdata->ctree, psdata->row,
2114                             psdata->column, psdata->folderview);
2115         g_free(psdata);
2116         return FALSE;
2117 }
2118
2119 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2120 {
2121         if (folderview->opened) {
2122                 if (dirty) {
2123                         folderview->opened = NULL;
2124                         return;
2125                 }
2126
2127                 FolderItem *olditem =
2128                         gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree), 
2129                                                       folderview->opened);
2130                 if (olditem) {
2131                         gchar *buf = g_strdup_printf(_("Closing folder %s..."), 
2132                                 olditem->path ? olditem->path:olditem->name);
2133                         /* will be null if we just moved the previously opened folder */
2134                         STATUSBAR_PUSH(folderview->mainwin, buf);
2135                         main_window_cursor_wait(folderview->mainwin);
2136                         g_free(buf);
2137                         summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2138                         summary_show(folderview->summaryview, NULL);
2139                         folder_item_close(olditem);
2140                         main_window_cursor_normal(folderview->mainwin);
2141                         STATUSBAR_POP(folderview->mainwin);
2142                         if (olditem->folder->klass->item_closed)
2143                                 olditem->folder->klass->item_closed(olditem);
2144
2145                 }
2146         }
2147
2148         if (folderview->opened &&
2149             !GTK_CMCTREE_ROW(folderview->opened)->children)
2150                 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2151
2152         folderview->opened = NULL;
2153 }
2154 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2155                                 gint column, FolderView *folderview)
2156 {
2157         static gboolean can_select = TRUE;      /* exclusive lock */
2158         gboolean opened;
2159         FolderItem *item;
2160         gchar *buf;
2161         int res = 0;
2162         GtkCMCTreeNode *old_opened = folderview->opened;
2163         START_TIMING("");
2164         folderview->selected = row;
2165
2166         debug_print("newly selected %p, opened %p\n", folderview->selected, 
2167                         folderview->opened);
2168         if (folderview->opened == row) {
2169                 folderview->open_folder = FALSE;
2170                 END_TIMING();
2171                 return;
2172         }
2173         
2174         item = gtk_cmctree_node_get_row_data(ctree, row);
2175         if (!item) {
2176                 END_TIMING();
2177                 folderview->open_folder = FALSE;
2178                 return;
2179         }
2180
2181         if (!can_select || summary_is_locked(folderview->summaryview)) {
2182                 if (folderview->opened) {
2183                         gtkut_ctree_set_focus_row(ctree, folderview->opened);
2184                         gtk_cmctree_select(ctree, folderview->opened);
2185                 }
2186                 folderview->open_folder = FALSE;
2187                 END_TIMING();
2188                 return;
2189         }
2190
2191         if (!folderview->open_folder) {
2192                 END_TIMING();
2193                 return;
2194         }
2195
2196         can_select = FALSE;
2197
2198         /* Save cache for old folder */
2199         /* We don't want to lose all caches if sylpheed crashed */
2200         /* resets folderview->opened to NULL */
2201         folderview_close_opened(folderview, FALSE);
2202         
2203         /* CLAWS: set compose button type: news folder items 
2204          * always have a news folder as parent */
2205         if (item->folder) 
2206                 toolbar_set_compose_button
2207                         (folderview->mainwin->toolbar,
2208                          FOLDER_TYPE(item->folder) == F_NEWS ? 
2209                          COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2210
2211         if (item->path)
2212                 debug_print("Folder %s is selected\n", item->path);
2213
2214         if (!GTK_CMCTREE_ROW(row)->children)
2215                 gtk_cmctree_expand(ctree, row);
2216
2217         /* ungrab the mouse event */
2218         if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2219                 gtk_grab_remove(GTK_WIDGET(ctree));
2220                 if (gdk_pointer_is_grabbed())
2221                         gdk_pointer_ungrab(GDK_CURRENT_TIME);
2222         }
2223
2224         /* Open Folder */
2225         /* TODO: wwp: avoid displaying (null) in the status bar */
2226         buf = g_strdup_printf(_("Opening folder %s..."), item->path ? 
2227                                         item->path : "(null)");
2228         debug_print("%s\n", buf);
2229         STATUSBAR_PUSH(folderview->mainwin, buf);
2230         g_free(buf);
2231
2232         main_window_cursor_wait(folderview->mainwin);
2233
2234         if (folderview->scanning_folder == item->folder) {
2235                 res = -2;
2236         } else {
2237                 res = folder_item_open(item);
2238         }
2239
2240         if (res == -1 && item->no_select == FALSE) {
2241                 main_window_cursor_normal(folderview->mainwin);
2242                 STATUSBAR_POP(folderview->mainwin);
2243
2244                 alertpanel_error(_("Folder could not be opened."));
2245
2246                 folderview->open_folder = FALSE;
2247                 can_select = TRUE;
2248                 END_TIMING();
2249                 return;
2250         } else if (res == -2 && item->no_select == FALSE) {
2251                 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2252                 data->ctree = ctree;
2253                 data->row = row;
2254                 data->column = column;
2255                 data->folderview = folderview;
2256                 debug_print("postponing open of %s till end of scan\n",
2257                         item->path ? item->path:item->name);
2258                 folderview->open_folder = FALSE;
2259                 can_select = TRUE;
2260                 if (folderview->postpone_select_id != 0)
2261                         g_source_remove(folderview->postpone_select_id);
2262                 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2263                 END_TIMING();
2264                 return;
2265         }
2266         
2267         main_window_cursor_normal(folderview->mainwin);
2268
2269         /* Show messages */
2270         summary_set_prefs_from_folderitem(folderview->summaryview, item);
2271         opened = summary_show(folderview->summaryview, item);
2272         
2273         folder_clean_cache_memory(item);
2274
2275         if (!opened) {
2276                 gtkut_ctree_set_focus_row(ctree, old_opened);
2277                 gtk_cmctree_select(ctree, old_opened);
2278                 folderview->opened = old_opened;
2279         } else {
2280                 folderview->opened = row;
2281                 if (gtk_cmctree_node_is_visible(ctree, row)
2282                     != GTK_VISIBILITY_FULL)
2283                         gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2284         }
2285
2286         STATUSBAR_POP(folderview->mainwin);
2287
2288         folderview->open_folder = FALSE;
2289         can_select = TRUE;
2290         END_TIMING();
2291 }
2292
2293 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2294                                      FolderView *folderview)
2295 {
2296         FolderItem *item;
2297
2298         item = gtk_cmctree_node_get_row_data(ctree, node);
2299         cm_return_if_fail(item != NULL);
2300         item->collapsed = FALSE;
2301         folderview_update_node(folderview, node);
2302 }
2303
2304 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2305                                       FolderView *folderview)
2306 {
2307         FolderItem *item;
2308
2309         item = gtk_cmctree_node_get_row_data(ctree, node);
2310         cm_return_if_fail(item != NULL);
2311         item->collapsed = TRUE;
2312         folderview_update_node(folderview, node);
2313 }
2314
2315 static void folderview_popup_close(GtkMenuShell *menu_shell,
2316                                    FolderView *folderview)
2317 {
2318         if (!folderview->opened) return;
2319
2320         gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2321 }
2322
2323 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2324                                    FolderView *folderview)
2325 {
2326         FolderColumnType type = folderview->col_state[column].type;
2327
2328         prefs_common.folder_col_size[type] = width;
2329 }
2330
2331 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2332 {
2333         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2334         gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2335         GtkCMCTreeNode *node, *parent_node;
2336         gint *col_pos = folderview->col_pos;
2337         FolderItemUpdateData hookdata;
2338
2339         parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2340         if (parent_node == NULL)
2341                 return;
2342
2343         gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2344
2345         text[col_pos[F_COL_FOLDER]] = item->name;
2346         node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2347                                      FOLDER_SPACING,
2348                                      folderxpm,
2349                                      folderopenxpm,
2350                                      FALSE, FALSE);
2351         gtk_cmctree_expand(ctree, parent_node);
2352         gtk_cmctree_node_set_row_data(ctree, node, item);
2353         if (normal_style)
2354                 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2355         folderview_sort_folders(folderview, parent_node, item->folder);
2356
2357         hookdata.item = item;
2358         hookdata.update_flags = F_ITEM_UPDATE_NAME;
2359         hookdata.msg = NULL;
2360         hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2361
2362         gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2363 }
2364
2365 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2366 {
2367         FolderView *folderview = (FolderView *)data;
2368         FolderItem *item;
2369         GSList *mlist = NULL;
2370         GSList *cur = NULL;
2371         FolderItem *special_trash = NULL;
2372         PrefsAccount *ac;
2373
2374         if (!folderview->selected) return;
2375         item = folderview_get_selected_item(folderview);
2376         cm_return_if_fail(item != NULL);
2377         cm_return_if_fail(item->folder != NULL);
2378
2379         if (NULL != (ac = account_find_from_item(item)))
2380                 special_trash = account_get_special_folder(ac, F_TRASH);
2381
2382         if (item != item->folder->trash && item != special_trash
2383         &&  !folder_has_parent_of_type(item, F_TRASH)) return;
2384         
2385         if (prefs_common.ask_on_clean) {
2386                 if (alertpanel(_("Empty trash"),
2387                                _("Delete all messages in trash?"),
2388                                GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2389                         return;
2390         }
2391         
2392         mlist = folder_item_get_msg_list(item);
2393         
2394         for (cur = mlist ; cur != NULL ; cur = cur->next) {
2395                 MsgInfo * msginfo = (MsgInfo *) cur->data;
2396                 if (MSG_IS_LOCKED(msginfo->flags))
2397                         continue;
2398                 /* is it partially received? (partial_recv isn't cached) */
2399                 if (msginfo->total_size != 0 && 
2400                     msginfo->size != (off_t)msginfo->total_size)
2401                         partial_mark_for_delete(msginfo);
2402         }
2403         procmsg_msg_list_free(mlist);
2404
2405         folder_item_remove_all_msg(item);
2406 }
2407
2408 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2409 {
2410         FolderView *folderview = (FolderView *)data;
2411         FolderItem *item;
2412         FolderItem *special_queue = NULL;
2413         PrefsAccount *ac;
2414         gchar *errstr = NULL;
2415
2416         if (!folderview->selected) return;
2417         item = folderview_get_selected_item(folderview);
2418         cm_return_if_fail(item != NULL);
2419         cm_return_if_fail(item->folder != NULL);
2420
2421         if (NULL != (ac = account_find_from_item(item)))
2422                 special_queue = account_get_special_folder(ac, F_QUEUE);
2423
2424         if (item != item->folder->queue && item != special_queue
2425         &&  !folder_has_parent_of_type(item, F_QUEUE)) return;
2426         
2427         if (procmsg_queue_is_empty(item))
2428                 return;
2429
2430         if (prefs_common.work_offline)
2431                 if (alertpanel(_("Offline warning"), 
2432                                _("You're working offline. Override?"),
2433                                GTK_STOCK_NO, GTK_STOCK_YES,
2434                                NULL) != G_ALERTALTERNATE)
2435                 return;
2436
2437         /* ask for confirmation before sending queued messages only
2438            in online mode and if there is at least one message queued
2439            in any of the folder queue
2440         */
2441         if (prefs_common.confirm_send_queued_messages) {
2442                 if (!prefs_common.work_offline) {
2443                         if (alertpanel(_("Send queued messages"), 
2444                                    _("Send all queued messages?"),
2445                                    GTK_STOCK_CANCEL, _("_Send"),
2446                                    NULL) != G_ALERTALTERNATE)
2447                                 return;
2448                 }
2449         }
2450
2451         if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2452                 if (!errstr)
2453                         alertpanel_error_log(_("Some errors occurred while "
2454                                            "sending queued messages."));
2455                 else {
2456                         alertpanel_error_log(_("Some errors occurred "
2457                                         "while sending queued messages:\n%s"), errstr);
2458                         g_free(errstr);
2459                 }
2460         }
2461 }
2462
2463 static void folderview_search_cb(GtkAction *action, gpointer data)
2464 {
2465         FolderView *folderview = (FolderView *)data;
2466         summary_search(folderview->summaryview);
2467 }
2468
2469 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2470 {
2471         FolderView *folderview = (FolderView *)data;
2472         FolderItem *item;
2473
2474         if (!folderview->selected) return;
2475
2476         item = folderview_get_selected_item(folderview);
2477         cm_return_if_fail(item != NULL);
2478         cm_return_if_fail(item->folder != NULL);
2479
2480         item->processing_pending = TRUE;
2481         folder_item_apply_processing(item);
2482         item->processing_pending = FALSE;
2483 }
2484
2485 static void folderview_property_cb(GtkAction *action, gpointer data)
2486 {
2487         FolderView *folderview = (FolderView *)data;
2488         FolderItem *item;
2489
2490         if (!folderview->selected) return;
2491
2492         item = folderview_get_selected_item(folderview);
2493         cm_return_if_fail(item != NULL);
2494         cm_return_if_fail(item->folder != NULL);
2495
2496         prefs_folder_item_open(item);
2497 }
2498
2499 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2500 {
2501         GSList *list = NULL;
2502         GSList *done = NULL;
2503         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2504         
2505         for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2506                 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2507                 &&  list->data != node) {
2508                         gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2509                         done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2510                 }
2511         }
2512         for (list = done; list != NULL; list = g_slist_next(list)) {
2513                 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse, 
2514                                                                  list->data);
2515         }
2516         g_slist_free(done);
2517 }
2518
2519 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2520                             FolderItem *to_folder, gboolean copy)
2521 {
2522         FolderItem *new_folder = NULL;
2523         gchar *buf;
2524         gint status;
2525
2526         cm_return_if_fail(folderview != NULL);
2527         cm_return_if_fail(from_folder != NULL);
2528         cm_return_if_fail(to_folder != NULL);
2529
2530         if (prefs_common.warn_dnd) {
2531                 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2532                                              _("Do you really want to make folder '%s' a subfolder of '%s'?"), 
2533                                         from_folder->name, to_folder->name);
2534                 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2535                                          GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2536                                          NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2537                 g_free(buf);
2538
2539                 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2540                         return;
2541                 else if (status & G_ALERTDISABLE)
2542                         prefs_common.warn_dnd = FALSE;
2543         }
2544
2545         buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."), 
2546                                 from_folder->name, to_folder->name);
2547         STATUSBAR_PUSH(folderview->mainwin, buf);
2548         g_free(buf);
2549         summary_clear_all(folderview->summaryview);
2550         folderview->opened = NULL;
2551         folderview->selected = NULL;
2552         gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2553         inc_lock();
2554         main_window_cursor_wait(folderview->mainwin);
2555
2556         statusbar_verbosity_set(FALSE);
2557         folder_item_update_freeze();
2558         gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2559         if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2560                 statusbar_verbosity_set(FALSE);
2561                 main_window_cursor_normal(folderview->mainwin);
2562                 STATUSBAR_POP(folderview->mainwin);
2563                 folder_item_update_thaw();
2564                 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2565
2566                 folderview_sort_folders(folderview, 
2567                         gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), 
2568                                 NULL, to_folder), new_folder->folder);
2569                 folderview_select(folderview, new_folder);
2570                 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2571         } else {
2572                 statusbar_verbosity_set(FALSE);         
2573                 main_window_cursor_normal(folderview->mainwin);
2574                 STATUSBAR_POP(folderview->mainwin);
2575                 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2576                 folder_item_update_thaw();
2577                 switch (status) {
2578                 case F_MOVE_FAILED_DEST_IS_PARENT:
2579                         alertpanel_error(_("Source and destination are the same."));
2580                         break;
2581                 case F_MOVE_FAILED_DEST_IS_CHILD:
2582                         alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2583                                                 _("Can't move a folder to one of its children."));
2584                         break;
2585                 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2586                         alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2587                         break;
2588                 default:
2589                         alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2590                         break;
2591                 }
2592         }       
2593         inc_unlock();           
2594         gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2595 }
2596
2597 static gint folderview_clist_compare(GtkCMCList *clist,
2598                                      gconstpointer ptr1, gconstpointer ptr2)
2599 {
2600         FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2601         FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2602
2603         if (item1->order > 0 && item2->order > 0)  // if we have an order item, use it
2604         {
2605                 return item1->order - item2->order;
2606         }
2607
2608         // if only one folder has an order it comes first
2609         if (item1->order > 0)
2610         {
2611                 return -1;
2612         }
2613         if (item2->order > 0)
2614         {
2615                 return 1;
2616         }
2617
2618         if (!item1->name)
2619                 return (item2->name != NULL);
2620         if (!item2->name)
2621                 return -1;
2622
2623         return g_utf8_collate(item1->name, item2->name);
2624 }
2625
2626 static void folderview_processing_cb(GtkAction *action, gpointer data)
2627 {
2628         FolderView *folderview = (FolderView *)data;
2629         FolderItem *item;
2630         gchar *id, *title;
2631
2632         if (!folderview->selected) return;
2633
2634         item = folderview_get_selected_item(folderview);
2635         cm_return_if_fail(item != NULL);
2636         cm_return_if_fail(item->folder != NULL);
2637
2638         id = folder_item_get_identifier(item);
2639         title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2640         g_free (id);
2641
2642         prefs_filtering_open(&item->prefs->processing, title,
2643                         MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2644         g_free (title);
2645 }
2646
2647 void folderview_set_target_folder_color(gint color_op) 
2648 {
2649         gint firstone = 1;
2650         GList *list;
2651         FolderView *folderview;
2652
2653         for (list = folderview_list; list != NULL; list = list->next) {
2654                 folderview = (FolderView *)list->data;
2655                 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2656                 if (firstone) {
2657                         bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2658                                 folderview->color_op;
2659                         firstone = 0;
2660                 }
2661         }
2662 }
2663
2664 static gchar *last_smallfont = NULL;
2665 static gchar *last_normalfont = NULL;
2666 static gchar *last_boldfont = NULL;
2667 static gboolean last_derive = 0;
2668
2669 void folderview_reinit_fonts(FolderView *folderview)
2670 {
2671         /* force reinit */
2672         g_free(last_smallfont);
2673         last_smallfont = NULL;
2674         g_free(last_normalfont);
2675         last_normalfont = NULL;
2676         g_free(last_boldfont);
2677         last_boldfont = NULL;
2678 }
2679
2680 void folderview_reflect_prefs(void)
2681 {
2682         gboolean update_font = FALSE;
2683         FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2684         FolderItem *item = folderview_get_selected_item(folderview);
2685         GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2686                                 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2687         gint height = gtk_adjustment_get_value(pos);
2688
2689         if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2690                         !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2691                         !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2692                         last_derive != prefs_common.derive_from_normal_font)
2693                 update_font = TRUE;
2694
2695         if (!update_font)
2696                 return;
2697
2698         g_free(last_smallfont);
2699         last_smallfont = g_strdup(SMALL_FONT);
2700         g_free(last_normalfont);
2701         last_normalfont = g_strdup(NORMAL_FONT);
2702         g_free(last_boldfont);
2703         last_boldfont = g_strdup(BOLD_FONT);
2704         last_derive = prefs_common.derive_from_normal_font;
2705
2706         normal_style = normal_color_style = bold_style =
2707                 bold_color_style = bold_tgtfold_style = NULL;
2708
2709         folderview_init(folderview);
2710         gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2711         folderview_column_set_titles(folderview);
2712         folderview_set_all();
2713
2714         g_signal_handlers_block_by_func
2715                 (G_OBJECT(folderview->ctree),
2716                  G_CALLBACK(folderview_selected), folderview);
2717
2718         if (item) {
2719                 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2720                         GTK_CMCTREE(folderview->ctree), NULL, item);
2721
2722                 folderview_select(folderview, item);
2723                 folderview->open_folder = FALSE;
2724                 folderview->selected = node;
2725         }
2726
2727         g_signal_handlers_unblock_by_func
2728                 (G_OBJECT(folderview->ctree),
2729                  G_CALLBACK(folderview_selected), folderview);
2730
2731         pos = gtk_scrolled_window_get_vadjustment(
2732                                 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2733         gtk_adjustment_set_value(pos, height);
2734         gtk_adjustment_changed(pos);
2735         gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2736 }
2737
2738 static void drag_state_stop(FolderView *folderview)
2739 {
2740         if (folderview->drag_timer_id)
2741                 g_source_remove(folderview->drag_timer_id);
2742         folderview->drag_timer_id = 0;
2743         folderview->drag_node = NULL;
2744 }
2745
2746 static gboolean folderview_defer_expand(FolderView *folderview)
2747 {
2748         if (folderview->drag_node) {
2749                 folderview_recollapse_nodes(folderview, folderview->drag_node);
2750                 if (folderview->drag_item->collapsed) {
2751                         gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2752                         folderview->nodes_to_recollapse = g_slist_append
2753                                 (folderview->nodes_to_recollapse, folderview->drag_node);
2754                 }
2755         }
2756         folderview->drag_item  = NULL;
2757         folderview->drag_timer_id = 0;
2758         return FALSE;
2759 }
2760
2761 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2762 {
2763         /* the idea is that we call drag_state_start() whenever we want expansion to
2764          * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2765          * we need to call drag_state_stop() */
2766         drag_state_stop(folderview);
2767         /* request expansion */
2768         if (0 != (folderview->drag_timer_id = g_timeout_add
2769                         (prefs_common.hover_timeout, 
2770                          (GSourceFunc)folderview_defer_expand,
2771                          folderview))) {
2772                 folderview->drag_node = node;
2773                 folderview->drag_item = item;
2774         }                        
2775 }
2776 #ifndef GENERIC_UMPC
2777 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2778                                   FolderView       *folderview)
2779 {
2780         GdkDragContext *context;
2781
2782         cm_return_if_fail(folderview != NULL);
2783         if (folderview->selected == NULL) return;
2784         if (folderview->nodes_to_recollapse) 
2785                 g_slist_free(folderview->nodes_to_recollapse);
2786         folderview->nodes_to_recollapse = NULL;
2787         context = gtk_drag_begin(widget, folderview->target_list,
2788                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2789         gtk_drag_set_icon_default(context);
2790 }
2791 #endif
2792 static void folderview_drag_data_get(GtkWidget        *widget,
2793                                      GdkDragContext   *drag_context,
2794                                      GtkSelectionData *selection_data,
2795                                      guint             info,
2796                                      guint             time,
2797                                      FolderView       *folderview)
2798 {
2799         FolderItem *item;
2800         GList *sel;
2801         gchar *source = NULL;
2802         if (info == TARGET_DUMMY) {
2803                 sel = GTK_CMCLIST(folderview->ctree)->selection;
2804                 if (!sel)
2805                         return;
2806
2807                 item = gtk_cmctree_node_get_row_data
2808                         (GTK_CMCTREE(folderview->ctree),
2809                          GTK_CMCTREE_NODE(sel->data));
2810                 if (item) {
2811                         source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2812                         gtk_selection_data_set(selection_data,
2813                                                gtk_selection_data_get_target(selection_data), 8,
2814                                                source, strlen(source));
2815                 }
2816         } else {
2817                 g_warning("unknown info %d", info);
2818         }
2819 }
2820
2821 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2822 {
2823         FolderUpdateData *hookdata;
2824         FolderView *folderview;
2825         GtkWidget *ctree;
2826
2827         hookdata = source;
2828         folderview = (FolderView *) userdata;   
2829         cm_return_val_if_fail(hookdata != NULL, FALSE);
2830         cm_return_val_if_fail(folderview != NULL, FALSE);
2831
2832         ctree = folderview->ctree;
2833         cm_return_val_if_fail(ctree != NULL, FALSE);
2834
2835         if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2836                 folderview_create_folder_node(folderview, hookdata->item);
2837         else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2838                 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2839                                 NULL, folder_item_parent(hookdata->item));
2840                 folderview_sort_folders(folderview, node, hookdata->folder);
2841         } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2842                 GtkCMCTreeNode *node;
2843
2844                 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2845                 if (node != NULL) {
2846                         gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2847                         if (folderview->selected == node)
2848                                 folderview->selected = NULL;
2849                         if (folderview->opened == node)
2850                                 folderview->opened = NULL;
2851                 }
2852         } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2853                 /* do nothing, it's done by the ADD and REMOVE) */
2854         } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2855                 folderview_set(folderview);
2856
2857         return FALSE;
2858 }
2859
2860 static gboolean folderview_dnd_scroll_cb(gpointer data)
2861 {
2862         FolderView *folderview = (FolderView *)data;
2863         GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2864                                 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2865         gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2866         gint max = (int)gtk_adjustment_get_upper(pos) -
2867                (int)gtk_adjustment_get_page_size(pos);
2868
2869         if (folderview->scroll_value == 0) {
2870                 folderview->scroll_timeout_id = 0;
2871                 return FALSE;
2872         }
2873
2874         if (folderview->scroll_value > 0 && new_val > max) {
2875                 new_val = max;
2876         } else if (folderview->scroll_value < 0 && new_val < 0) {
2877                 new_val = 0;
2878         }
2879         gtk_adjustment_set_value(pos, new_val);
2880         
2881         return TRUE;
2882 }
2883
2884 static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
2885                                           GdkDragContext *context,
2886                                           gint            x,
2887                                           gint            y,
2888                                           guint           time,
2889                                           FolderView     *folderview)
2890 {
2891         gint row, column;
2892         FolderItem *item = NULL, *src_item = NULL;
2893         GtkCMCTreeNode *node = NULL;
2894         gboolean acceptable = FALSE;
2895         GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2896                                 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2897         int height = (int)gtk_adjustment_get_page_size(pos);
2898         int total_height = (int)gtk_adjustment_get_upper(pos);
2899         int vpos = (int)gtk_adjustment_get_value(pos);
2900         int offset = prefs_common.show_col_headers ? 24:0;
2901         int dist;
2902
2903         if (gtk_cmclist_get_selection_info
2904                 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2905                 GtkWidget *srcwidget;
2906
2907                 if (y > height - (48 - offset) && height + vpos < total_height) {
2908                         dist = -(height - (48 - offset) - y);
2909                         folderview->scroll_value = 1.41f * (1+(dist / 6));
2910                 } else if (y < 72 - (24 - offset) && y >= 0) {
2911                         dist = 72 - (24 - offset) - y;
2912                         folderview->scroll_value = -1.41f * (1+(dist / 6));
2913                 } else {
2914                         folderview->scroll_value = 0;
2915                 }
2916                 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2917                         folderview->scroll_timeout_id = 
2918                                 g_timeout_add(30, folderview_dnd_scroll_cb,
2919                                               folderview);
2920                 }
2921
2922                 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2923                 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2924                 src_item = folderview->summaryview->folder_item;
2925
2926                 srcwidget = gtk_drag_get_source_widget(context);
2927                 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2928                         /* comes from summaryview */
2929                         /* we are copying messages, so only accept folder items that are not
2930                            the source item, are no root items and can copy messages */
2931                         if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2932                             src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2933                             FOLDER_TYPE(item->folder) != F_UNKNOWN)
2934                                 acceptable = TRUE;
2935                 } else if (srcwidget == folderview->ctree) {
2936                         /* comes from folderview */
2937                         /* we are moving folder items, only accept folders that are not
2938                            the source items and can copy messages and create folder items */
2939                         if (item && item->folder && src_item && src_item != item &&
2940                             FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2941                             FOLDER_CLASS(item->folder)->create_folder != NULL &&
2942                             ((FOLDER_TYPE(item->folder) != F_UNKNOWN &&  FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2943                              || item->folder == src_item->folder))
2944                                 acceptable = TRUE;
2945                 } else {
2946                         /* comes from another app */
2947                         /* we are adding messages, so only accept folder items that are 
2948                            no root items and can copy messages */
2949                         if (item && item->folder && folder_item_parent(item) != NULL
2950                             && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2951                             FOLDER_TYPE(item->folder) != F_UNKNOWN)
2952                                 acceptable = TRUE;
2953                 }
2954         }
2955
2956         if (acceptable || (src_item && src_item == item))
2957                 drag_state_start(folderview, node, item);
2958         
2959         if (acceptable) {
2960                 g_signal_handlers_block_by_func
2961                         (G_OBJECT(widget),
2962                          G_CALLBACK(folderview_selected), folderview);
2963                 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2964                 g_signal_handlers_unblock_by_func
2965                         (G_OBJECT(widget),
2966                          G_CALLBACK(folderview_selected), folderview);
2967                 gdk_drag_status(context, 
2968                                         (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2969                                         GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2970         } else {
2971                 if (folderview->opened)
2972                         gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2973                 gdk_drag_status(context, 0, time);
2974         }
2975
2976         return acceptable;
2977 }
2978
2979 static void folderview_drag_leave_cb(GtkWidget      *widget,
2980                                      GdkDragContext *context,
2981                                      guint           time,
2982                                      FolderView     *folderview)
2983 {
2984         drag_state_stop(folderview);
2985         folderview->scroll_value = 0;
2986         gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2987 }
2988
2989 static void free_info (gpointer stuff, gpointer data)
2990 {
2991         g_free(stuff);
2992 }
2993
2994 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2995                            guint time, FolderItem *item)
2996 {
2997         GList *list, *tmp;
2998         GSList *msglist = NULL;
2999         list = uri_list_extract_filenames(data);
3000         if (!(item && item->folder && folder_item_parent(item) != NULL
3001                     && FOLDER_CLASS(item->folder)->add_msg != NULL))
3002         {
3003                 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3004                 debug_print("item doesn't fit\n");                      
3005                 return;
3006         }       
3007         if (!list) {
3008                 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3009                 debug_print("list is empty\n");                 
3010                 return;
3011         }
3012         for (tmp = list; tmp != NULL; tmp = tmp->next) {
3013                 MsgFileInfo *info = NULL;
3014
3015                 if (file_is_email((gchar *)tmp->data)) {
3016                         info = g_new0(MsgFileInfo, 1);
3017                         info->msginfo = NULL;
3018                         info->file = (gchar *)tmp->data;
3019                         msglist = g_slist_prepend(msglist, info);
3020                         debug_print("file is a mail\n");
3021                 } else {
3022                         debug_print("file isn't a mail\n");
3023                 }
3024         }
3025         if (msglist) {
3026                 msglist = g_slist_reverse(msglist);
3027                 folder_item_add_msgs(item, msglist, FALSE);
3028                 g_slist_foreach(msglist, free_info, NULL);
3029                 g_slist_free(msglist);
3030                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3031         } else {
3032                 gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
3033         }
3034         list_free_strings(list);
3035         g_list_free(list);
3036 }
3037
3038 static void folderview_drag_received_cb(GtkWidget        *widget,
3039                                         GdkDragContext   *drag_context,
3040                                         gint              x,
3041                                         gint              y,
3042                                         GtkSelectionData *data,
3043                                         guint             info,
3044                                         guint             time,
3045                                         FolderView       *folderview)
3046 {
3047         gint row, column;
3048         FolderItem *item = NULL, *src_item;
3049         GtkCMCTreeNode *node;
3050         int offset = prefs_common.show_col_headers ? 24:0;
3051
3052         folderview->scroll_value = 0;
3053
3054         if (info == TARGET_DUMMY) {
3055                 drag_state_stop(folderview);
3056                 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3057                 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3058                         /* comes from summaryview */
3059                         if (gtk_cmclist_get_selection_info
3060                                 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3061                                 return;
3062
3063                         node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3064                         item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3065                         src_item = folderview->summaryview->folder_item;
3066
3067                         if (item->no_select) {
3068                                 alertpanel_error(_("The destination folder can only be used to "
3069                                                    "store subfolders."));
3070                                 return;
3071                         }
3072                         /* re-check (due to acceptable possibly set for folder moves */
3073                         if (!(item && item->folder && item->path && !item->no_select && 
3074                               src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3075                                 return;
3076                         }
3077                         if (item && src_item) {
3078                                 switch (gdk_drag_context_get_selected_action(drag_context)) {
3079                                 case GDK_ACTION_COPY:
3080                                         summary_copy_selected_to(folderview->summaryview, item);
3081                                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
3082                                         break;
3083                                 case GDK_ACTION_MOVE:
3084                                 case GDK_ACTION_DEFAULT:
3085                                 default:
3086                                         if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3087                                                 summary_copy_selected_to(folderview->summaryview, item);
3088                                         else
3089                                                 summary_move_selected_to(folderview->summaryview, item);
3090                                         gtk_drag_finish(drag_context, TRUE, TRUE, time);
3091                                 }
3092                         } else
3093                                 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3094                 } else {
3095                         /* comes from folderview */
3096                         char *source;
3097                         gboolean folder_is_normal = TRUE;
3098                         gboolean copy = (GDK_ACTION_COPY ==
3099                                 gdk_drag_context_get_selected_action(drag_context));
3100
3101                         source = (char *)gtk_selection_data_get_data(data) + 17;
3102                         if (gtk_cmclist_get_selection_info
3103                             (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3104                             || *source == 0) {
3105                                 gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
3106                                 return;
3107                         }
3108                         node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3109                         item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3110                         src_item = folder_find_item_from_identifier(source);
3111
3112                         folder_is_normal = 
3113                                 src_item != NULL &&
3114                                 src_item->stype == F_NORMAL &&
3115                                 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3116                                 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3117                                 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3118                                 !folder_has_parent_of_type(src_item, F_TRASH);
3119                         if (!item || !src_item || !folder_is_normal) {
3120                                 gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
3121                                 return;
3122                         }
3123
3124                         folderview_move_folder(folderview, src_item, item, copy);
3125                         gtk_drag_finish(drag_context, TRUE, TRUE, time);
3126                 }
3127                 folderview->nodes_to_recollapse = NULL;
3128         } else if (info == TARGET_MAIL_URI_LIST) {
3129                 if (gtk_cmclist_get_selection_info
3130                         (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3131                         return;
3132
3133                 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3134                 if (!node) {
3135                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
3136                         debug_print("no node\n");               
3137                         return;
3138                 }
3139                 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3140                 if (!item) {
3141                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
3142                         debug_print("no item\n");
3143                         return;
3144                 }
3145                 folderview_finish_dnd(gtk_selection_data_get_data(data),
3146                         drag_context, time, item);
3147         }
3148 }
3149
3150 static void folderview_drag_end_cb(GtkWidget        *widget, 
3151                                    GdkDragContext   *drag_context,
3152                                    FolderView       *folderview)
3153 {
3154         drag_state_stop(folderview);
3155         folderview->scroll_value = 0;
3156         g_slist_free(folderview->nodes_to_recollapse);
3157         folderview->nodes_to_recollapse = NULL;
3158 }
3159
3160 void folderview_register_popup(FolderViewPopup *fpopup)
3161 {
3162         GList *folderviews;
3163
3164         for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3165                 FolderView *folderview = folderviews->data;
3166                 GtkActionGroup *factory;
3167
3168                 factory = create_action_group(folderview, fpopup);
3169                 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3170         }       
3171         g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3172 }
3173
3174 void folderview_unregister_popup(FolderViewPopup *fpopup)
3175 {
3176         GList *folderviews;
3177
3178
3179         for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3180                 FolderView *folderview = folderviews->data;
3181
3182                 g_hash_table_remove(folderview->popups, fpopup->klass);
3183         }       
3184         g_hash_table_remove(folderview_popups, fpopup->klass);
3185 }
3186
3187 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3188 {
3189         g_return_if_fail(folderview != NULL);
3190         g_return_if_fail(item != NULL);
3191
3192         GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3193         g_return_if_fail(ctree != NULL);
3194
3195         GtkCMCTreeNode *node =
3196                 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3197         g_return_if_fail(node != NULL);
3198
3199         gtk_cmctree_remove_node(ctree, node);
3200 }
3201
3202 void folderview_freeze(FolderView *folderview)
3203 {
3204         if (folderview)
3205                 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3206 }
3207
3208 void folderview_thaw(FolderView *folderview)
3209 {
3210         if (folderview)
3211                 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3212 }
3213
3214 void folderview_grab_focus(FolderView *folderview)
3215 {
3216          if (folderview)
3217                  gtk_widget_grab_focus(folderview->ctree);
3218 }