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