2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
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.
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.
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/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtkwidget.h>
26 #include <gtk/gtkscrolledwindow.h>
27 #include <gtk/gtkctree.h>
28 #include <gtk/gtkcontainer.h>
29 #include <gtk/gtkclist.h>
30 #include <gtk/gtkstyle.h>
31 #include <gtk/gtksignal.h>
32 #include <gtk/gtkmain.h>
33 #include <gtk/gtkstatusbar.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
42 #include "mainwindow.h"
43 #include "folderview.h"
44 #include "summaryview.h"
45 #include "summary_search.h"
46 #include "inputdialog.h"
47 #include "manage_window.h"
48 #include "alertpanel.h"
50 #include "stock_pixmap.h"
54 #include "prefs_common.h"
55 #include "prefs_account.h"
56 #include "prefs_filtering.h"
57 #include "prefs_folder_item.h"
60 #include "foldersel.h"
62 #include "statusbar.h"
64 #include "folderutils.h"
65 #include "partial_download.h"
66 #include "prefs_folder_column.h"
67 #include "filtering.h"
68 #include "quicksearch.h"
73 #define COL_FOLDER_WIDTH 150
74 #define COL_NUM_WIDTH 32
76 static GList *folderview_list = NULL;
78 static GtkStyle *normal_style;
79 static GtkStyle *normal_color_style;
80 static GtkStyle *bold_style;
81 static GtkStyle *bold_color_style;
82 static GtkStyle *bold_tgtfold_style;
84 static GdkPixmap *inboxxpm;
85 static GdkBitmap *inboxxpmmask;
86 static GdkPixmap *inboxhrmxpm;
87 static GdkBitmap *inboxhrmxpmmask;
88 static GdkPixmap *inboxopenxpm;
89 static GdkBitmap *inboxopenxpmmask;
90 static GdkPixmap *inboxopenhrmxpm;
91 static GdkBitmap *inboxopenhrmxpmmask;
92 static GdkPixmap *outboxxpm;
93 static GdkBitmap *outboxxpmmask;
94 static GdkPixmap *outboxhrmxpm;
95 static GdkBitmap *outboxhrmxpmmask;
96 static GdkPixmap *outboxopenxpm;
97 static GdkBitmap *outboxopenxpmmask;
98 static GdkPixmap *outboxopenhrmxpm;
99 static GdkBitmap *outboxopenhrmxpmmask;
100 static GdkPixmap *folderxpm;
101 static GdkBitmap *folderxpmmask;
102 static GdkPixmap *folderhrmxpm;
103 static GdkBitmap *folderhrmxpmmask;
104 static GdkPixmap *folderopenxpm;
105 static GdkBitmap *folderopenxpmmask;
106 static GdkPixmap *folderopenhrmxpm;
107 static GdkBitmap *folderopenhrmxpmmask;
108 static GdkPixmap *trashopenxpm;
109 static GdkBitmap *trashopenxpmmask;
110 static GdkPixmap *trashopenhrmxpm;
111 static GdkBitmap *trashopenhrmxpmmask;
112 static GdkPixmap *trashxpm;
113 static GdkBitmap *trashxpmmask;
114 static GdkPixmap *trashhrmxpm;
115 static GdkBitmap *trashhrmxpmmask;
116 static GdkPixmap *queuexpm;
117 static GdkBitmap *queuexpmmask;
118 static GdkPixmap *queuehrmxpm;
119 static GdkBitmap *queuehrmxpmmask;
120 static GdkPixmap *queueopenxpm;
121 static GdkBitmap *queueopenxpmmask;
122 static GdkPixmap *queueopenhrmxpm;
123 static GdkBitmap *queueopenhrmxpmmask;
124 static GdkPixmap *draftsxpm;
125 static GdkBitmap *draftsxpmmask;
126 static GdkPixmap *draftsopenxpm;
127 static GdkBitmap *draftsopenxpmmask;
128 static GdkPixmap *noselectxpm;
129 static GdkBitmap *noselectxpmmask;
131 static GdkPixmap *m_inboxxpm;
132 static GdkBitmap *m_inboxxpmmask;
133 static GdkPixmap *m_inboxhrmxpm;
134 static GdkBitmap *m_inboxhrmxpmmask;
135 static GdkPixmap *m_inboxopenxpm;
136 static GdkBitmap *m_inboxopenxpmmask;
137 static GdkPixmap *m_inboxopenhrmxpm;
138 static GdkBitmap *m_inboxopenhrmxpmmask;
139 static GdkPixmap *m_outboxxpm;
140 static GdkBitmap *m_outboxxpmmask;
141 static GdkPixmap *m_outboxhrmxpm;
142 static GdkBitmap *m_outboxhrmxpmmask;
143 static GdkPixmap *m_outboxopenxpm;
144 static GdkBitmap *m_outboxopenxpmmask;
145 static GdkPixmap *m_outboxopenhrmxpm;
146 static GdkBitmap *m_outboxopenhrmxpmmask;
147 static GdkPixmap *m_folderxpm;
148 static GdkBitmap *m_folderxpmmask;
149 static GdkPixmap *m_folderhrmxpm;
150 static GdkBitmap *m_folderhrmxpmmask;
151 static GdkPixmap *m_folderopenxpm;
152 static GdkBitmap *m_folderopenxpmmask;
153 static GdkPixmap *m_folderopenhrmxpm;
154 static GdkBitmap *m_folderopenhrmxpmmask;
155 static GdkPixmap *m_trashopenxpm;
156 static GdkBitmap *m_trashopenxpmmask;
157 static GdkPixmap *m_trashopenhrmxpm;
158 static GdkBitmap *m_trashopenhrmxpmmask;
159 static GdkPixmap *m_trashxpm;
160 static GdkBitmap *m_trashxpmmask;
161 static GdkPixmap *m_trashhrmxpm;
162 static GdkBitmap *m_trashhrmxpmmask;
163 static GdkPixmap *m_queuexpm;
164 static GdkBitmap *m_queuexpmmask;
165 static GdkPixmap *m_queuehrmxpm;
166 static GdkBitmap *m_queuehrmxpmmask;
167 static GdkPixmap *m_queueopenxpm;
168 static GdkBitmap *m_queueopenxpmmask;
169 static GdkPixmap *m_queueopenhrmxpm;
170 static GdkBitmap *m_queueopenhrmxpmmask;
171 static GdkPixmap *m_draftsxpm;
172 static GdkBitmap *m_draftsxpmmask;
173 static GdkPixmap *m_draftsopenxpm;
174 static GdkBitmap *m_draftsopenxpmmask;
176 static GdkPixmap *newxpm;
177 static GdkBitmap *newxpmmask;
178 static GdkPixmap *unreadxpm;
179 static GdkBitmap *unreadxpmmask;
180 static GdkPixmap *readxpm;
181 static GdkBitmap *readxpmmask;
183 static void folderview_select_node (FolderView *folderview,
185 static void folderview_set_folders (FolderView *folderview);
186 static void folderview_sort_folders (FolderView *folderview,
189 static void folderview_append_folder (FolderView *folderview,
191 static void folderview_update_node (FolderView *folderview,
194 static gint folderview_clist_compare (GtkCList *clist,
198 /* callback functions */
199 static gboolean folderview_button_pressed (GtkWidget *ctree,
200 GdkEventButton *event,
201 FolderView *folderview);
202 static gboolean folderview_button_released (GtkWidget *ctree,
203 GdkEventButton *event,
204 FolderView *folderview);
205 static gboolean folderview_key_pressed (GtkWidget *widget,
207 FolderView *folderview);
208 static void folderview_selected (GtkCTree *ctree,
211 FolderView *folderview);
212 static void folderview_tree_expanded (GtkCTree *ctree,
214 FolderView *folderview);
215 static void folderview_tree_collapsed (GtkCTree *ctree,
217 FolderView *folderview);
218 static void folderview_popup_close (GtkMenuShell *menu_shell,
219 FolderView *folderview);
220 static void folderview_col_resized (GtkCList *clist,
223 FolderView *folderview);
225 static void mark_all_read_cb (FolderView *folderview,
229 static void folderview_empty_trash_cb (FolderView *folderview,
233 static void folderview_send_queue_cb (FolderView *folderview,
237 static void folderview_search_cb (FolderView *folderview,
240 static void folderview_run_processing_cb(FolderView *folderview,
244 static void folderview_property_cb (FolderView *folderview,
248 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
249 GdkDragContext *context,
253 FolderView *folderview);
254 static void folderview_drag_leave_cb (GtkWidget *widget,
255 GdkDragContext *context,
257 FolderView *folderview);
258 static void folderview_drag_received_cb (GtkWidget *widget,
259 GdkDragContext *drag_context,
262 GtkSelectionData *data,
265 FolderView *folderview);
267 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
268 FolderView *folderview);
270 static void folderview_drag_data_get (GtkWidget *widget,
271 GdkDragContext *drag_context,
272 GtkSelectionData *selection_data,
275 FolderView *folderview);
276 static void folderview_drag_end_cb (GtkWidget *widget,
277 GdkDragContext *drag_context,
278 FolderView *folderview);
280 static void folderview_create_folder_node (FolderView *folderview,
282 static gboolean folderview_update_folder (gpointer source,
284 static gboolean folderview_update_item_claws (gpointer source,
286 static void folderview_processing_cb(FolderView *folderview, guint action,
288 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
289 GdkEventButton *event);
291 GHashTable *folderview_popups;
293 static GtkItemFactoryEntry folderview_common_popup_entries[] =
295 {N_("/Mark all re_ad"), NULL, mark_all_read_cb, 0, NULL},
296 {"/---", NULL, NULL, 0, "<Separator>"},
297 {N_("/R_un processing rules"), NULL, folderview_run_processing_cb, 0, NULL},
298 {N_("/_Search folder..."), NULL, folderview_search_cb, 0, NULL},
299 {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL},
300 {N_("/Process_ing..."), NULL, folderview_processing_cb, 0, NULL},
303 static GtkItemFactoryEntry folder_view_trash_popup_entries[] = {
304 {"/------trashsep", NULL, NULL, 0, "<Separator>"},
305 {N_("/Empty _trash..."), NULL, folderview_empty_trash_cb, 0, NULL},
308 static GtkItemFactoryEntry folder_view_queue_popup_entries[] = {
309 {"/------queuesep", NULL, NULL, 0, "<Separator>"},
310 {N_("/Send _queue..."), NULL, folderview_send_queue_cb, 0, NULL},
314 GtkTargetEntry folderview_drag_types[] =
316 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
317 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
320 void folderview_initialize(void)
322 FolderViewPopup *fpopup;
324 GSList *entries = NULL;
326 fpopup = g_new0(FolderViewPopup, 1);
328 n_entries = sizeof(folderview_common_popup_entries) /
329 sizeof(folderview_common_popup_entries[0]);
330 for (i = 0; i < n_entries; i++)
331 entries = g_slist_append(entries, &folderview_common_popup_entries[i]);
333 fpopup->klass = "common";
334 fpopup->path = "<CommonFolder>";
335 fpopup->entries = entries;
336 fpopup->set_sensitivity = NULL;
338 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
339 g_hash_table_insert(folderview_popups, "common", fpopup);
342 static GtkItemFactory *create_ifactory(FolderView *folderview, FolderViewPopup *fpopup)
345 GtkItemFactory *factory;
346 FolderViewPopup *fpopup_common;
349 factory = gtk_item_factory_new(GTK_TYPE_MENU, fpopup->path, NULL);
350 gtk_item_factory_set_translate_func(factory, menu_translate,
353 for (entries = fpopup->entries; entries != NULL; entries = g_slist_next(entries))
354 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
356 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
357 if (fpopup_common != fpopup)
358 for (entries = fpopup_common->entries; entries != NULL; entries = g_slist_next(entries))
359 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
361 popup = gtk_item_factory_get_widget(factory, fpopup->path);
362 g_signal_connect(G_OBJECT(popup), "selection_done",
363 G_CALLBACK(folderview_popup_close),
369 static void create_ifactories(gpointer key, gpointer value, gpointer data)
371 FolderView *folderview = data;
372 FolderViewPopup *fpopup = value;
373 GtkItemFactory *factory;
375 factory = create_ifactory(folderview, fpopup);
376 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
379 static void folderview_column_set_titles(FolderView *folderview)
381 GtkWidget *ctree = folderview->ctree;
382 GtkWidget *label_folder;
383 GtkWidget *label_new;
384 GtkWidget *label_unread;
385 GtkWidget *label_total;
386 GtkWidget *hbox_folder;
388 GtkWidget *hbox_unread;
389 GtkWidget *hbox_total;
390 gint *col_pos = folderview->col_pos;
392 debug_print("setting titles...\n");
393 gtk_widget_realize(folderview->ctree);
394 gtk_widget_show_all(folderview->scrolledwin);
396 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
397 * instead text (text overflows making them unreadable and ugly) */
398 stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
399 &newxpm, &newxpmmask);
400 stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
401 &unreadxpm, &unreadxpmmask);
402 stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
403 &readxpm, &readxpmmask);
405 label_folder = gtk_label_new(_("Folder"));
406 label_new = gtk_image_new_from_pixmap(newxpm, newxpmmask);
407 label_unread = gtk_image_new_from_pixmap(unreadxpm, unreadxpmmask);
408 label_total = gtk_image_new_from_pixmap(readxpm, readxpmmask);
410 gtk_clist_column_titles_active(GTK_CLIST(ctree));
412 hbox_folder = gtk_hbox_new(FALSE, 4);
413 hbox_new = gtk_hbox_new(FALSE, 4);
414 hbox_unread = gtk_hbox_new(FALSE, 4);
415 hbox_total = gtk_hbox_new(FALSE, 4);
418 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
419 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
420 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
421 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
422 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
423 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
424 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
425 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
427 gtk_widget_show_all(hbox_folder);
428 gtk_widget_show_all(hbox_new);
429 gtk_widget_show_all(hbox_unread);
430 gtk_widget_show_all(hbox_total);
433 gtk_widget_set_size_request(hbox_new, -1, 20);
434 gtk_widget_set_size_request(hbox_unread, -1, 20);
435 gtk_widget_set_size_request(hbox_total, -1, 20);
438 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
439 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_NEW],hbox_new);
440 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
441 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
447 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
448 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
449 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
452 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
454 FolderView *folderview = (FolderView *)data;
455 GdkEventButton event;
456 if (folderview_get_selected_item(folderview) == NULL)
460 event.time = gtk_get_current_event_time();
462 folderview_set_sens_and_popup_menu(folderview, -1,
469 static GtkWidget *folderview_ctree_create(FolderView *folderview)
473 FolderColumnState *col_state;
474 FolderColumnType type;
475 gchar *titles[N_FOLDER_COLS];
477 GtkWidget *scrolledwin = folderview->scrolledwin;
479 debug_print("creating tree...\n");
480 memset(titles, 0, sizeof(titles));
482 col_state = prefs_folder_column_get_config();
483 memset(titles, 0, sizeof(titles));
485 col_pos = folderview->col_pos;
487 for (i = 0; i < N_FOLDER_COLS; i++) {
488 folderview->col_state[i] = col_state[i];
489 type = col_state[i].type;
493 titles[col_pos[F_COL_FOLDER]] = _("Folder");
494 titles[col_pos[F_COL_NEW]] = _("New");
495 titles[col_pos[F_COL_UNREAD]] = _("Unread");
496 /* TRANSLATORS: This in Number sign in American style */
497 titles[col_pos[F_COL_TOTAL]] = _("#");
499 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
502 if (prefs_common.show_col_headers == FALSE)
503 gtk_clist_column_titles_hide(GTK_CLIST(ctree));
506 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
507 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[F_COL_NEW],
509 gtk_clist_set_column_justification(GTK_CLIST(ctree),
510 col_pos[F_COL_UNREAD],
512 gtk_clist_set_column_justification(GTK_CLIST(ctree),
513 col_pos[F_COL_TOTAL],
515 if (prefs_common.enable_dotted_lines) {
516 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
517 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
518 GTK_CTREE_EXPANDER_SQUARE);
520 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
521 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
522 GTK_CTREE_EXPANDER_TRIANGLE);
525 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
526 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
528 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
529 gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
531 /* don't let title buttons take key focus */
532 for (i = 0; i < N_FOLDER_COLS; i++) {
533 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
535 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[i],
536 prefs_common.folder_col_size[i]);
537 gtk_clist_set_column_visibility
538 (GTK_CLIST(ctree), i, col_state[i].visible);
541 g_signal_connect(G_OBJECT(ctree), "key_press_event",
542 G_CALLBACK(folderview_key_pressed),
544 g_signal_connect(G_OBJECT(ctree), "button_press_event",
545 G_CALLBACK(folderview_button_pressed),
548 g_signal_connect(G_OBJECT(ctree), "popup-menu",
549 G_CALLBACK(folderview_popup_menu), folderview);
551 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
552 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
553 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
554 G_CALLBACK(folderview_popup_menu), folderview);
556 g_signal_connect(G_OBJECT(ctree), "button_release_event",
557 G_CALLBACK(folderview_button_released),
559 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
560 G_CALLBACK(folderview_selected), folderview);
562 /* drag-n-dropping folders on maemo is impractical as this
563 * opens the folder almost everytime */
564 g_signal_connect(G_OBJECT(ctree), "start_drag",
565 G_CALLBACK(folderview_start_drag), folderview);
567 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
568 G_CALLBACK(folderview_drag_data_get),
571 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
572 G_CALLBACK(folderview_tree_expanded),
574 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
575 G_CALLBACK(folderview_tree_collapsed),
578 g_signal_connect(G_OBJECT(ctree), "resize_column",
579 G_CALLBACK(folderview_col_resized),
583 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
584 folderview_drag_types, 2,
585 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
586 g_signal_connect(G_OBJECT(ctree), "drag_motion",
587 G_CALLBACK(folderview_drag_motion_cb),
589 g_signal_connect(G_OBJECT(ctree), "drag_leave",
590 G_CALLBACK(folderview_drag_leave_cb),
592 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
593 G_CALLBACK(folderview_drag_received_cb),
595 g_signal_connect(G_OBJECT(ctree), "drag_end",
596 G_CALLBACK(folderview_drag_end_cb),
599 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
604 void folderview_set_column_order(FolderView *folderview)
607 FolderItem *item = folderview_get_selected_item(folderview);
608 GtkWidget *scrolledwin = folderview->scrolledwin;
610 debug_print("recreating tree...\n");
611 gtk_widget_destroy(folderview->ctree);
613 folderview->ctree = ctree = folderview_ctree_create(folderview);
614 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
615 GTK_CLIST(ctree)->hadjustment);
616 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
617 GTK_CLIST(ctree)->vadjustment);
618 gtk_widget_show(ctree);
620 folderview_set(folderview);
621 folderview_column_set_titles(folderview);
623 folderview_select(folderview,item);
626 FolderView *folderview_create(void)
628 FolderView *folderview;
629 GtkWidget *scrolledwin;
632 debug_print("Creating folder view...\n");
633 folderview = g_new0(FolderView, 1);
635 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
636 gtk_scrolled_window_set_policy
637 (GTK_SCROLLED_WINDOW(scrolledwin),
638 GTK_POLICY_AUTOMATIC,
639 prefs_common.folderview_vscrollbar_policy);
640 gtk_widget_set_size_request(scrolledwin,
641 prefs_common.folderview_width,
642 prefs_common.folderview_height);
644 folderview->scrolledwin = scrolledwin;
645 ctree = folderview_ctree_create(folderview);
647 /* create popup factories */
648 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
649 g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
651 folderview->ctree = ctree;
653 folderview->folder_update_callback_id =
654 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
655 folderview->folder_item_update_callback_id =
656 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
658 gtk_widget_show_all(scrolledwin);
660 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
661 folderview_list = g_list_append(folderview_list, folderview);
662 folderview->deferred_refresh_id = -1;
667 void folderview_init(FolderView *folderview)
669 GtkWidget *ctree = folderview->ctree;
672 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
673 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
674 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
675 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
676 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
677 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
678 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
679 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
680 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
681 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
682 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
683 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
686 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
687 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
688 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
689 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
690 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
691 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
692 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
693 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
694 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
696 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
697 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
698 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
699 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
700 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
701 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
702 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
703 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
704 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
705 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
706 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
707 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
708 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
709 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
710 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
711 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
712 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
713 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
714 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
715 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
716 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
717 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
720 PangoFontDescription *font_desc;
721 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
722 font_desc = pango_font_description_from_string(NORMAL_FONT);
724 if (normal_style->font_desc)
725 pango_font_description_free
726 (normal_style->font_desc);
727 normal_style->font_desc = font_desc;
729 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
730 normal_color_style = gtk_style_copy(normal_style);
731 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
733 gtk_widget_set_style(ctree, normal_style);
737 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
738 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
739 pango_font_description_set_weight
740 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
741 bold_color_style = gtk_style_copy(bold_style);
742 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
744 bold_tgtfold_style = gtk_style_copy(bold_style);
745 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
749 static gboolean folderview_defer_set(gpointer data)
751 FolderView *folderview = (FolderView *)data;
752 MainWindow *mainwin = folderview->mainwin;
756 if (mainwin->lock_count)
759 debug_print("doing deferred folderview_set now\n");
760 folderview_set(folderview);
762 folderview->deferred_refresh_id = -1;
766 void folderview_set(FolderView *folderview)
768 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
769 MainWindow *mainwin = folderview->mainwin;
770 FolderItem *sel_item = NULL, *op_item = NULL;
775 if (mainwin->lock_count) {
776 if (folderview->deferred_refresh_id == -1)
777 folderview->deferred_refresh_id =
778 g_timeout_add(500, folderview_defer_set, folderview);
779 debug_print("deferred folderview_set\n");
784 debug_print("Setting folder info...\n");
785 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
787 main_window_cursor_wait(mainwin);
789 if (folderview->selected)
790 sel_item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
791 if (folderview->opened)
792 op_item = gtk_ctree_node_get_row_data(ctree, folderview->opened);
794 folderview->selected = NULL;
795 folderview->opened = NULL;
797 gtk_clist_freeze(GTK_CLIST(ctree));
798 gtk_clist_clear(GTK_CLIST(ctree));
800 folderview_set_folders(folderview);
803 folderview->selected = gtk_ctree_find_by_row_data(ctree, NULL, sel_item);
805 folderview->opened = gtk_ctree_find_by_row_data(ctree, NULL, op_item);
807 gtk_clist_thaw(GTK_CLIST(ctree));
808 main_window_cursor_normal(mainwin);
809 STATUSBAR_POP(mainwin);
813 void folderview_set_all(void)
817 for (list = folderview_list; list != NULL; list = list->next)
818 folderview_set((FolderView *)list->data);
821 void folderview_select(FolderView *folderview, FolderItem *item)
823 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
825 GtkCTreeNode *old_selected = folderview->selected;
829 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
830 if (node) folderview_select_node(folderview, node);
832 if (old_selected != node)
833 folder_update_op_count();
836 static void mark_all_read_cb(FolderView *folderview, guint action,
842 item = folderview_get_selected_item(folderview);
846 if (folderview->summaryview->folder_item != item
847 && prefs_common.ask_mark_all_read) {
848 val = alertpanel_full(_("Mark all as read"),
849 _("Do you really want to mark all mails in this "
850 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
851 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
853 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
855 else if (val & G_ALERTDISABLE)
856 prefs_common.ask_mark_all_read = FALSE;
859 summary_lock(folderview->summaryview);
860 folder_item_update_freeze();
861 if (folderview->summaryview->folder_item == item)
862 summary_freeze(folderview->summaryview);
863 folderutils_mark_all_read(item);
864 if (folderview->summaryview->folder_item == item)
865 summary_thaw(folderview->summaryview);
866 folder_item_update_thaw();
867 summary_unlock(folderview->summaryview);
870 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
872 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
874 g_return_if_fail(node != NULL);
876 if (folderview->open_folder) {
880 folderview->open_folder = TRUE;
881 gtkut_ctree_set_focus_row(ctree, node);
882 gtk_ctree_select(ctree, node);
883 if (folderview->summaryview->folder_item &&
884 folderview->summaryview->folder_item->total_msgs > 0)
885 summary_grab_focus(folderview->summaryview);
887 gtk_widget_grab_focus(folderview->ctree);
889 gtkut_ctree_expand_parent_all(ctree, node);
892 void folderview_unselect(FolderView *folderview)
894 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
896 (GTK_CTREE(folderview->ctree), folderview->opened);
898 folderview->selected = folderview->opened = NULL;
901 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
907 node = gtkut_ctree_node_next(ctree, node);
909 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
911 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
912 item = gtk_ctree_node_get_row_data(ctree, node);
913 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
920 void folderview_select_next_marked(FolderView *folderview)
922 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
923 GtkCTreeNode *node = NULL;
924 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
925 gboolean last_open = prefs_common.always_show_msg;
927 prefs_common.summary_select_prio[0] = ACTION_MARKED;
928 prefs_common.always_show_msg = TRUE;
930 if ((node = folderview_find_next_marked(ctree, folderview->opened))
932 folderview_select_node(folderview, node);
936 if (!folderview->opened ||
937 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
940 /* search again from the first node */
941 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
942 folderview_select_node(folderview, node);
945 prefs_common.summary_select_prio[0] = last_summary_select_prio;
946 prefs_common.always_show_msg = last_open;
949 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
955 node = gtkut_ctree_node_next(ctree, node);
957 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
959 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
960 item = gtk_ctree_node_get_row_data(ctree, node);
961 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
968 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
970 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
971 GtkCTreeNode *node = NULL;
972 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
973 gboolean last_open = prefs_common.always_show_msg;
975 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
976 prefs_common.always_show_msg = force_open ? TRUE : last_open;
978 if ((node = folderview_find_next_unread(ctree, folderview->opened))
980 folderview_select_node(folderview, node);
984 if (!folderview->opened ||
985 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
988 /* search again from the first node */
989 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
990 folderview_select_node(folderview, node);
993 prefs_common.summary_select_prio[0] = last_summary_select_prio;
994 prefs_common.always_show_msg = last_open;
997 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
1003 node = gtkut_ctree_node_next(ctree, node);
1005 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1007 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1008 item = gtk_ctree_node_get_row_data(ctree, node);
1009 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
1016 void folderview_select_next_new(FolderView *folderview)
1018 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1019 GtkCTreeNode *node = NULL;
1020 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1021 gboolean last_open = prefs_common.always_show_msg;
1023 prefs_common.summary_select_prio[0] = ACTION_NEW;
1024 prefs_common.always_show_msg = TRUE;
1026 if ((node = folderview_find_next_new(ctree, folderview->opened))
1028 folderview_select_node(folderview, node);
1032 if (!folderview->opened ||
1033 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1036 /* search again from the first node */
1037 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1038 folderview_select_node(folderview, node);
1041 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1042 prefs_common.always_show_msg = last_open;
1045 FolderItem *folderview_get_selected_item(FolderView *folderview)
1047 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1049 if (!folderview->selected) return NULL;
1050 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1053 static void folderview_set_folders(FolderView *folderview)
1056 list = folder_get_list();
1058 for (; list != NULL; list = list->next) {
1059 folderview_append_folder(folderview, FOLDER(list->data));
1063 static gchar *get_scan_str(FolderItem *item)
1066 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1067 item->folder->name, G_DIR_SEPARATOR,
1070 return g_strdup_printf(_("Scanning folder %s ..."),
1071 item->folder->name);
1073 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1077 for (list = folderview_list; list != NULL; list = list->next) {
1078 FolderView *folderview = (FolderView *)list->data;
1079 MainWindow *mainwin = folderview->mainwin;
1080 gchar *str = get_scan_str(item);
1082 STATUSBAR_PUSH(mainwin, str);
1083 STATUSBAR_POP(mainwin);
1088 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1091 MainWindow *mainwin = mainwindow_get_mainwindow();
1092 FolderView *folderview = NULL;
1093 GtkAdjustment *pos = NULL;
1096 g_return_if_fail(folder != NULL);
1098 if (!folder->klass->scan_tree) return;
1101 alertpanel_full(_("Rebuild folder tree"),
1102 _("Rebuilding the folder tree will remove "
1103 "local caches. Do you want to continue?"),
1104 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1105 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1106 != G_ALERTALTERNATE) {
1112 window = label_window_create(_("Rebuilding folder tree..."));
1114 window = label_window_create(_("Scanning folder tree..."));
1117 folderview = mainwin->folderview;
1120 pos = gtk_scrolled_window_get_vadjustment(
1121 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1122 height = pos->value;
1125 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1126 folder_scan_tree(folder, rebuild);
1127 folder_set_ui_func(folder, NULL, NULL);
1129 folderview_set_all();
1132 pos = gtk_scrolled_window_get_vadjustment(
1133 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1134 gtk_adjustment_set_value(pos, height);
1135 gtk_adjustment_changed(pos);
1137 label_window_destroy(window);
1141 void folderview_fast_rescan_tree(Folder *folder)
1144 MainWindow *mainwin = mainwindow_get_mainwindow();
1145 FolderView *folderview = NULL;
1146 GtkAdjustment *pos = NULL;
1149 g_return_if_fail(folder != NULL);
1151 if (!folder->klass->scan_tree) return;
1155 window = label_window_create(_("Scanning folder tree..."));
1158 folderview = mainwin->folderview;
1161 pos = gtk_scrolled_window_get_vadjustment(
1162 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1163 height = pos->value;
1166 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1167 folder_fast_scan_tree(folder);
1168 folder_set_ui_func(folder, NULL, NULL);
1170 folderview_set_all();
1173 pos = gtk_scrolled_window_get_vadjustment(
1174 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1175 gtk_adjustment_set_value(pos, height);
1176 gtk_adjustment_changed(pos);
1178 label_window_destroy(window);
1182 /** folderview_check_new()
1183 * Scan and update the folder and return the
1184 * count the number of new messages since last check.
1185 * \param folder the folder to check for new messages
1186 * \return the number of new messages since last check
1188 gint folderview_check_new(Folder *folder)
1192 FolderView *folderview;
1196 gint former_new_msgs = 0;
1197 gint former_new = 0, former_unread = 0, former_total;
1199 for (list = folderview_list; list != NULL; list = list->next) {
1200 folderview = (FolderView *)list->data;
1201 ctree = GTK_CTREE(folderview->ctree);
1202 folderview->scanning_folder = folder;
1204 main_window_lock(folderview->mainwin);
1206 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1207 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1209 item = gtk_ctree_node_get_row_data(ctree, node);
1210 if (!item || !item->path || !item->folder) continue;
1211 if (item->no_select) continue;
1212 if (folder && folder != item->folder) continue;
1213 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1214 if (!item->prefs->newmailcheck) continue;
1215 if (item->processing_pending == TRUE) {
1216 debug_print("skipping %s, processing pending\n",
1217 item->path ? item->path : item->name);
1220 if (item->scanning == TRUE) {
1221 debug_print("skipping %s, scanning\n",
1222 item->path ? item->path : item->name);
1226 str = get_scan_str(item);
1228 STATUSBAR_PUSH(folderview->mainwin, str);
1232 folderview_scan_tree_func(item->folder, item, NULL);
1233 former_new = item->new_msgs;
1234 former_unread = item->unread_msgs;
1235 former_total = item->total_msgs;
1237 if (item->folder->klass->scan_required &&
1238 (item->folder->klass->scan_required(item->folder, item) ||
1239 item->folder->inbox == item ||
1240 item->opened == TRUE ||
1241 item->processing_pending == TRUE)) {
1242 if (folder_item_scan(item) < 0) {
1244 summaryview_unlock(folderview->summaryview, item);
1245 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1246 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1247 item->path ? item->path:item->name);
1248 STATUSBAR_POP(folderview->mainwin);
1250 } else if (!FOLDER_IS_LOCAL(folder)) {
1251 STATUSBAR_POP(folderview->mainwin);
1256 } else if (!item->folder->klass->scan_required) {
1257 if (folder_item_scan(item) < 0) {
1258 summaryview_unlock(folderview->summaryview, item);
1259 if (folder && !FOLDER_IS_LOCAL(folder)) {
1260 STATUSBAR_POP(folderview->mainwin);
1265 if (former_new != item->new_msgs ||
1266 former_unread != item->unread_msgs ||
1267 former_total != item->total_msgs)
1268 folderview_update_node(folderview, node);
1270 new_msgs += item->new_msgs;
1271 former_new_msgs += former_new;
1272 STATUSBAR_POP(folderview->mainwin);
1274 folderview->scanning_folder = NULL;
1275 main_window_unlock(folderview->mainwin);
1279 folder_write_list();
1280 /* Number of new messages since last check is the just the difference
1281 * between former_new_msgs and new_msgs. If new_msgs is less than
1282 * former_new_msgs, that would mean another session accessed the folder
1283 * and the result is not well defined.
1285 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1289 void folderview_check_new_all(void)
1293 FolderView *folderview;
1295 folderview = (FolderView *)folderview_list->data;
1298 main_window_lock(folderview->mainwin);
1299 window = label_window_create
1300 (_("Checking for new messages in all folders..."));
1302 list = folder_get_list();
1303 for (; list != NULL; list = list->next) {
1304 Folder *folder = list->data;
1306 folderview_check_new(folder);
1309 folder_write_list();
1310 folderview_set_all();
1312 label_window_destroy(window);
1313 main_window_unlock(folderview->mainwin);
1317 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1323 if (!item || !item->folder || !item->folder->node)
1326 node = item->folder->node;
1328 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1329 node = node->children;
1332 (item->new_msgs > 0 ||
1333 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1337 while (node != NULL) {
1338 if (node && node->data) {
1339 FolderItem *next_item = (FolderItem*) node->data;
1341 if (folderview_have_new_children_sub(folderview,
1350 static gboolean folderview_have_new_children(FolderView *folderview,
1353 return folderview_have_new_children_sub(folderview, item, FALSE);
1356 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1362 if (!item || !item->folder || !item->folder->node)
1365 node = item->folder->node;
1367 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1368 node = node->children;
1371 (item->unread_msgs > 0 ||
1372 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1376 while (node != NULL) {
1377 if (node && node->data) {
1378 FolderItem *next_item = (FolderItem*) node->data;
1380 if (folderview_have_unread_children_sub(folderview,
1390 static gboolean folderview_have_unread_children(FolderView *folderview,
1393 return folderview_have_unread_children_sub(folderview, item, FALSE);
1396 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1402 if (!item || !item->folder || !item->folder->node)
1405 node = item->folder->node;
1407 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1408 node = node->children;
1410 if (in_sub && item->search_match){
1414 while (node != NULL) {
1415 if (node && node->data) {
1416 FolderItem *next_item = (FolderItem*) node->data;
1418 if (folderview_have_matching_children_sub(folderview,
1428 static gboolean folderview_have_matching_children(FolderView *folderview,
1431 return folderview_have_matching_children_sub(folderview, item, FALSE);
1434 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1440 if (!item || !item->folder || !item->folder->node)
1443 node = item->folder->node;
1445 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1446 node = node->children;
1448 if (item->marked_msgs != 0) {
1452 while (node != NULL) {
1453 if (node && node->data) {
1454 FolderItem *next_item = (FolderItem*) node->data;
1456 if (folderview_have_marked_children_sub(folderview,
1465 static gboolean folderview_have_marked_children(FolderView *folderview,
1468 return folderview_have_marked_children_sub(folderview, item, FALSE);
1471 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1473 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1474 GtkStyle *style = NULL;
1475 GtkStyle *color_style = NULL;
1477 GdkPixmap *xpm, *openxpm;
1478 GdkBitmap *mask, *openmask;
1479 static GdkPixmap *searchicon;
1480 static GdkBitmap *searchmask;
1481 gboolean mark = FALSE;
1484 gboolean add_unread_mark;
1485 gboolean add_sub_match_mark;
1486 gboolean use_bold, use_color;
1487 gint *col_pos = folderview->col_pos;
1488 SpecialFolderItemType stype;
1490 item = gtk_ctree_node_get_row_data(ctree, node);
1491 g_return_if_fail(item != NULL);
1493 if (!GTK_CTREE_ROW(node)->expanded)
1494 mark = folderview_have_marked_children(folderview, item);
1496 mark = (item->marked_msgs != 0);
1498 stype = item->stype;
1499 if (stype == F_NORMAL) {
1500 if (folder_has_parent_of_type(item, F_TRASH))
1502 else if (folder_has_parent_of_type(item, F_DRAFT))
1504 else if (folder_has_parent_of_type(item, F_OUTBOX))
1506 else if (folder_has_parent_of_type(item, F_QUEUE))
1511 if (item->hide_read_msgs) {
1512 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1513 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1514 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1515 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1517 xpm = mark?m_inboxxpm:inboxxpm;
1518 mask = mark?m_inboxxpmmask:inboxxpmmask;
1519 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1520 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1524 if (item->hide_read_msgs) {
1525 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1526 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1527 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1528 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1530 xpm = mark?m_outboxxpm:outboxxpm;
1531 mask = mark?m_outboxxpmmask:outboxxpmmask;
1532 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1533 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1537 if (item->hide_read_msgs) {
1538 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1539 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1540 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1541 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1543 xpm = mark?m_queuexpm:queuexpm;
1544 mask = mark?m_queuexpmmask:queuexpmmask;
1545 openxpm = mark?m_queueopenxpm:queueopenxpm;
1546 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1550 if (item->hide_read_msgs) {
1551 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1552 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1553 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1554 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1556 xpm = mark?m_trashxpm:trashxpm;
1557 mask = mark?m_trashxpmmask:trashxpmmask;
1558 openxpm = mark?m_trashopenxpm:trashopenxpm;
1559 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1563 xpm = mark?m_draftsxpm:draftsxpm;
1564 mask = mark?m_draftsxpmmask:draftsxpmmask;
1565 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1566 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1569 if (item->hide_read_msgs) {
1570 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1571 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1572 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1573 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1575 xpm = mark?m_folderxpm:folderxpm;
1576 mask = mark?m_folderxpmmask:folderxpmmask;
1577 openxpm = mark?m_folderopenxpm:folderopenxpm;
1578 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1582 if (item->no_select) {
1583 xpm = openxpm = noselectxpm;
1584 mask = openmask = noselectxpmmask;
1587 name = folder_item_get_name(item);
1589 if (!GTK_CTREE_ROW(node)->expanded) {
1590 add_unread_mark = folderview_have_unread_children(
1592 add_sub_match_mark = folderview_have_matching_children(
1595 add_unread_mark = FALSE;
1596 add_sub_match_mark = FALSE;
1599 if (item->search_match) {
1601 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1602 &searchicon, &searchmask);
1604 xpm = openxpm = searchicon;
1605 mask = openmask = searchmask;
1609 if (prefs_common.display_folder_unread) {
1610 if (folder_has_parent_of_type(item, F_QUEUE)) {
1611 /* only total_msgs matters here */
1612 if (item->total_msgs > 0) {
1613 /* show total number (should be equal to the unread number)
1615 str = g_strdup_printf("%s (%d%s%s)",
1616 name, item->total_msgs,
1617 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1618 (item->unreadmarked_msgs > 0) ? "!" : "");
1621 if (prefs_common.display_folder_unread == 1) {
1622 if (item->unread_msgs > 0) {
1623 /* show unread number and signs */
1624 str = g_strdup_printf("%s (%d%s%s)",
1625 name, item->unread_msgs,
1626 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1627 (item->unreadmarked_msgs > 0) ? "!" : "");
1630 if (item->total_msgs > 0) {
1631 /* show unread number, total number and signs if any */
1632 str = g_strdup_printf("%s (%d/%d%s%s)",
1633 name, item->unread_msgs, item->total_msgs,
1634 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1635 (item->unreadmarked_msgs > 0) ? "!" : "");
1639 if ((str == NULL) &&
1640 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1641 /* no unread/total numbers, but at least one sign */
1642 str = g_strdup_printf("%s (%s%s)",
1644 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1645 (item->unreadmarked_msgs > 0) ? "!" : "");
1649 /* last fallback, folder name only or with ! sign */
1650 str = g_strdup_printf("%s%s",
1651 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1653 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1654 xpm, mask, openxpm, openmask,
1655 FALSE, GTK_CTREE_ROW(node)->expanded);
1659 if (!folder_item_parent(item)) {
1660 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1661 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1662 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1664 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1665 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1666 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1669 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1670 folder_has_parent_of_type(item, F_DRAFT) ||
1671 folder_has_parent_of_type(item, F_TRASH)) {
1672 use_bold = use_color = FALSE;
1673 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1674 /* highlight queue folder if there are any messages */
1675 use_bold = use_color = (item->total_msgs > 0);
1677 /* if unread messages exist, print with bold font */
1678 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1680 /* if new messages exist, print with colored letter */
1682 (item->new_msgs > 0) ||
1684 folderview_have_new_children(folderview, item));
1687 gtk_ctree_node_set_foreground(ctree, node, NULL);
1692 if (item->prefs->color > 0 && !use_color) {
1693 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1694 color_style = gtk_style_copy(bold_style);
1695 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1696 style = color_style;
1697 } else if (use_color) {
1698 style = bold_color_style;
1701 if (item->op_count > 0) {
1702 style = bold_tgtfold_style;
1704 } else if (use_color) {
1705 style = normal_color_style;
1706 gtk_ctree_node_set_foreground(ctree, node,
1707 &folderview->color_new);
1708 } else if (item->op_count > 0) {
1709 style = bold_tgtfold_style;
1710 } else if (item->prefs->color > 0) {
1712 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1713 color_style = gtk_style_copy(normal_style);
1714 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1715 style = color_style;
1717 style = normal_style;
1720 gtk_ctree_node_set_row_style(ctree, node, style);
1722 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1723 folderview_update_node(folderview, node);
1726 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1729 FolderView *folderview;
1733 g_return_if_fail(item != NULL);
1735 for (list = folderview_list; list != NULL; list = list->next) {
1736 folderview = (FolderView *)list->data;
1737 ctree = GTK_CTREE(folderview->ctree);
1739 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1741 item->search_match = matches;
1742 folderview_update_node(folderview, node);
1747 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1749 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1750 FolderView *folderview = (FolderView *)data;
1753 g_return_val_if_fail(update_info != NULL, TRUE);
1754 g_return_val_if_fail(update_info->item != NULL, TRUE);
1755 g_return_val_if_fail(folderview != NULL, FALSE);
1757 ctree = GTK_CTREE(folderview->ctree);
1759 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1762 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1763 folderview_update_node(folderview, node);
1765 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1766 update_info->item == folderview->summaryview->folder_item &&
1767 update_info->item != NULL)
1768 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1769 summary_show(folderview->summaryview, update_info->item);
1775 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1776 GNode *gnode, GtkCTreeNode *cnode,
1779 FolderView *folderview = (FolderView *)data;
1780 FolderItem *item = FOLDER_ITEM(gnode->data);
1782 g_return_val_if_fail(item != NULL, FALSE);
1784 gtk_ctree_node_set_row_data(ctree, cnode, item);
1785 folderview_update_node(folderview, cnode);
1790 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1793 FolderView *folderview = (FolderView *)data;
1796 if (GTK_CTREE_ROW(node)->children) {
1797 item = gtk_ctree_node_get_row_data(ctree, node);
1798 g_return_if_fail(item != NULL);
1800 if (!item->collapsed)
1801 gtk_ctree_expand(ctree, node);
1803 folderview_update_node(folderview, node);
1807 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1808 GtkCTreeNode *root, GtkCTreeNode **prev)
1811 GtkCTreeNode *node, *parent, *sibling;
1813 node = gtk_ctree_find_by_row_data(ctree, root, item);
1815 g_warning("%s not found.\n", item->path);
1817 parent = GTK_CTREE_ROW(node)->parent;
1818 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1819 sibling = GTK_CTREE_ROW(*prev)->sibling;
1821 sibling = GTK_CTREE_ROW(parent)->children;
1825 tmp = gtk_ctree_node_get_row_data
1827 if (tmp->stype != F_NORMAL)
1828 sibling = GTK_CTREE_ROW(sibling)->sibling;
1832 if (node != sibling)
1833 gtk_ctree_move(ctree, node, parent, sibling);
1840 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1843 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1844 GtkCTreeNode *prev = NULL;
1846 gtk_clist_freeze(GTK_CLIST(ctree));
1847 gtk_sctree_sort_recursive(ctree, root);
1848 if (root && GTK_CTREE_ROW(root)->parent) {
1849 gtk_clist_thaw(GTK_CLIST(ctree));
1852 set_special_folder(ctree, folder->inbox, root, &prev);
1853 set_special_folder(ctree, folder->outbox, root, &prev);
1854 set_special_folder(ctree, folder->draft, root, &prev);
1855 set_special_folder(ctree, folder->queue, root, &prev);
1856 set_special_folder(ctree, folder->trash, root, &prev);
1857 gtk_clist_thaw(GTK_CLIST(ctree));
1860 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1862 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1865 g_return_if_fail(folder != NULL);
1867 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1868 folderview_gnode_func, folderview);
1869 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1871 folderview_sort_folders(folderview, root, folder);
1874 /* callback functions */
1875 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1876 GdkEventButton *event)
1878 GtkCList *clist = GTK_CLIST(folderview->ctree);
1881 FolderViewPopup *fpopup;
1882 GtkItemFactory *fpopup_factory;
1884 FolderItem *special_trash = NULL, *special_queue = NULL;
1888 item = gtk_clist_get_row_data(clist, row);
1890 item = folderview_get_selected_item(folderview);
1892 g_return_if_fail(item != NULL);
1893 g_return_if_fail(item->folder != NULL);
1894 folder = item->folder;
1896 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1898 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1900 fpopup = g_hash_table_lookup(folderview_popups, "common");
1901 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1904 if (fpopup->set_sensitivity != NULL)
1905 fpopup->set_sensitivity(fpopup_factory, item);
1907 if (NULL != (ac = account_find_from_item(item))) {
1908 special_trash = account_get_special_folder(ac, F_TRASH);
1909 special_queue = account_get_special_folder(ac, F_QUEUE);
1912 if ((item == folder->trash || item == special_trash
1913 || folder_has_parent_of_type(item, F_TRASH)) &&
1914 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1915 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1916 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1917 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1918 && !folder_has_parent_of_type(item, F_TRASH)) {
1919 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1920 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1923 if ((item == folder->queue || item == special_queue
1924 || folder_has_parent_of_type(item, F_QUEUE)) &&
1925 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1926 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1927 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1928 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1929 && !folder_has_parent_of_type(item, F_QUEUE)) {
1930 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1931 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1934 #define SET_SENS(name, sens) \
1935 menu_set_sensitive(fpopup_factory, name, sens)
1937 if ( FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_TYPE(item->folder) == F_IMAP )
1938 SET_SENS("/Download messages", !item->no_select);
1940 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1941 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1942 folderview->selected == folderview->opened);
1943 SET_SENS("/Properties...", !item->no_select);
1945 SET_SENS("/Run processing rules", item->prefs->processing &&
1946 item->total_msgs >= 1 && !item->processing_pending);
1947 SET_SENS("/Processing...", item->node->parent != NULL &&
1948 !item->no_select && !item->processing_pending);
1950 if (item == folder->trash || item == special_trash
1951 || folder_has_parent_of_type(item, F_TRASH)) {
1952 GSList *msglist = folder_item_get_msg_list(item);
1953 SET_SENS("/Empty trash...", msglist != NULL);
1954 procmsg_msg_list_free(msglist);
1956 if (item == folder->queue || item == special_queue
1957 || folder_has_parent_of_type(item, F_QUEUE)) {
1958 GSList *msglist = folder_item_get_msg_list(item);
1959 SET_SENS("/Send queue...", msglist != NULL);
1960 procmsg_msg_list_free(msglist);
1964 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1965 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1966 event->button, event->time);
1969 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1970 FolderView *folderview)
1972 GtkCList *clist = GTK_CLIST(ctree);
1973 gint prev_row = -1, row = -1, column = -1;
1975 if (!event) return FALSE;
1977 if (event->button == 1 || event->button == 2) {
1978 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1979 folderview->open_folder = TRUE;
1981 if (event->type == GDK_2BUTTON_PRESS) {
1982 if (clist->selection) {
1985 node = GTK_CTREE_NODE(clist->selection->data);
1987 gtk_ctree_toggle_expansion(
1990 folderview->open_folder = FALSE;
1997 if (event->button == 2 || event->button == 3) {
1999 if (clist->selection) {
2002 node = GTK_CTREE_NODE(clist->selection->data);
2004 prev_row = gtkut_ctree_get_nth_from_node
2005 (GTK_CTREE(ctree), node);
2008 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
2011 if (prev_row != row) {
2012 gtk_clist_unselect_all(clist);
2013 if (event->button == 2)
2014 folderview_select_node
2016 gtk_ctree_node_nth(GTK_CTREE(ctree),
2019 gtk_clist_select_row(clist, row, column);
2023 if (event->button != 3) return FALSE;
2025 folderview_set_sens_and_popup_menu(folderview, row, event);
2029 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2030 FolderView *folderview)
2032 int row = -1, column = -1;
2034 if (!event) return FALSE;
2036 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2039 if (event->button == 1 && folderview->open_folder == FALSE &&
2040 folderview->opened != NULL) {
2041 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2042 folderview->opened);
2043 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2049 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2050 FolderView *folderview)
2052 if (!event) return FALSE;
2054 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2057 switch (event->keyval) {
2063 if (folderview->selected) {
2064 folderview_select_node(folderview,
2065 folderview->selected);
2070 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2071 gtk_ctree_toggle_expansion(
2072 GTK_CTREE(folderview->ctree),
2073 folderview->selected);
2078 if (folderview->selected) {
2079 if (folderview->opened == folderview->selected &&
2080 (!folderview->summaryview->folder_item ||
2081 folderview->summaryview->folder_item->total_msgs == 0))
2082 folderview_select_next_unread(folderview, TRUE);
2084 folderview_select_node(folderview,
2085 folderview->selected);
2095 typedef struct _PostponedSelectData
2100 FolderView *folderview;
2101 } PostponedSelectData;
2103 static gboolean postpone_select(void *data)
2105 PostponedSelectData *psdata = (PostponedSelectData *)data;
2106 debug_print("trying again\n");
2107 psdata->folderview->open_folder = TRUE;
2108 main_window_cursor_normal(psdata->folderview->mainwin);
2109 STATUSBAR_POP(psdata->folderview->mainwin);
2110 folderview_selected(psdata->ctree, psdata->row,
2111 psdata->column, psdata->folderview);
2116 void folderview_close_opened(FolderView *folderview)
2118 if (folderview->opened) {
2119 FolderItem *olditem;
2121 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2122 folderview->opened);
2124 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2125 olditem->path ? olditem->path:olditem->name);
2126 /* will be null if we just moved the previously opened folder */
2127 STATUSBAR_PUSH(folderview->mainwin, buf);
2128 main_window_cursor_wait(folderview->mainwin);
2130 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2131 summary_show(folderview->summaryview, NULL);
2132 folder_item_close(olditem);
2133 main_window_cursor_normal(folderview->mainwin);
2134 STATUSBAR_POP(folderview->mainwin);
2138 if (folderview->opened &&
2139 !GTK_CTREE_ROW(folderview->opened)->children)
2140 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2142 folderview->opened = NULL;
2144 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2145 gint column, FolderView *folderview)
2147 static gboolean can_select = TRUE; /* exclusive lock */
2152 GtkCTreeNode *old_opened = folderview->opened;
2154 folderview->selected = row;
2156 debug_print("newly selected %p, opened %p\n", folderview->selected,
2157 folderview->opened);
2158 if (folderview->opened == row) {
2159 folderview->open_folder = FALSE;
2164 item = gtk_ctree_node_get_row_data(ctree, row);
2167 folderview->open_folder = FALSE;
2171 if (!can_select || summary_is_locked(folderview->summaryview)) {
2172 if (folderview->opened) {
2173 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2174 gtk_ctree_select(ctree, folderview->opened);
2176 folderview->open_folder = FALSE;
2181 if (!folderview->open_folder) {
2188 /* Save cache for old folder */
2189 /* We don't want to lose all caches if sylpheed crashed */
2190 /* resets folderview->opened to NULL */
2191 folderview_close_opened(folderview);
2193 /* CLAWS: set compose button type: news folder items
2194 * always have a news folder as parent */
2196 toolbar_set_compose_button
2197 (folderview->mainwin->toolbar,
2198 FOLDER_TYPE(item->folder) == F_NEWS ?
2199 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2202 debug_print("Folder %s is selected\n", item->path);
2204 if (!GTK_CTREE_ROW(row)->children)
2205 gtk_ctree_expand(ctree, row);
2207 /* ungrab the mouse event */
2208 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2209 gtk_grab_remove(GTK_WIDGET(ctree));
2210 if (gdk_pointer_is_grabbed())
2211 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2215 /* TODO: wwp: avoid displaying (null) in the status bar */
2216 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2217 item->path : "(null)");
2218 debug_print("%s\n", buf);
2219 STATUSBAR_PUSH(folderview->mainwin, buf);
2222 main_window_cursor_wait(folderview->mainwin);
2224 if (folderview->scanning_folder == item->folder) {
2227 res = folder_item_open(item);
2230 if (res == -1 && item->no_select == FALSE) {
2231 main_window_cursor_normal(folderview->mainwin);
2232 STATUSBAR_POP(folderview->mainwin);
2234 alertpanel_error(_("Folder could not be opened."));
2236 folderview->open_folder = FALSE;
2240 } else if (res == -2 && item->no_select == FALSE) {
2241 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2242 data->ctree = ctree;
2244 data->column = column;
2245 data->folderview = folderview;
2246 debug_print("postponing open of %s till end of scan\n",
2247 item->path ? item->path:item->name);
2248 folderview->open_folder = FALSE;
2250 g_timeout_add(500, postpone_select, data);
2255 main_window_cursor_normal(folderview->mainwin);
2258 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2259 opened = summary_show(folderview->summaryview, item);
2261 folder_clean_cache_memory(item);
2264 gtkut_ctree_set_focus_row(ctree, old_opened);
2265 gtk_ctree_select(ctree, old_opened);
2266 folderview->opened = old_opened;
2268 folderview->opened = row;
2269 if (gtk_ctree_node_is_visible(ctree, row)
2270 != GTK_VISIBILITY_FULL)
2271 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2274 STATUSBAR_POP(folderview->mainwin);
2276 folderview->open_folder = FALSE;
2281 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2282 FolderView *folderview)
2286 item = gtk_ctree_node_get_row_data(ctree, node);
2287 g_return_if_fail(item != NULL);
2288 item->collapsed = FALSE;
2289 folderview_update_node(folderview, node);
2292 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2293 FolderView *folderview)
2297 item = gtk_ctree_node_get_row_data(ctree, node);
2298 g_return_if_fail(item != NULL);
2299 item->collapsed = TRUE;
2300 folderview_update_node(folderview, node);
2303 static void folderview_popup_close(GtkMenuShell *menu_shell,
2304 FolderView *folderview)
2306 if (!folderview->opened) return;
2308 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2311 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2312 FolderView *folderview)
2314 FolderColumnType type = folderview->col_state[column].type;
2316 prefs_common.folder_col_size[type] = width;
2319 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2323 folderview_create_folder_node(folderview, item);
2325 if (!item || !item->folder || !item->folder->node)
2328 srcnode = item->folder->node;
2329 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2330 srcnode = srcnode->children;
2331 while (srcnode != NULL) {
2332 if (srcnode && srcnode->data) {
2333 FolderItem *next_item = (FolderItem*) srcnode->data;
2334 folderview_create_folder_node_recursive(folderview, next_item);
2336 srcnode = srcnode->next;
2340 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2342 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2343 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2344 GtkCTreeNode *node, *parent_node;
2345 gint *col_pos = folderview->col_pos;
2346 FolderItemUpdateData hookdata;
2348 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2349 if (parent_node == NULL)
2352 gtk_clist_freeze(GTK_CLIST(ctree));
2354 text[col_pos[F_COL_FOLDER]] = item->name;
2355 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2357 folderxpm, folderxpmmask,
2358 folderopenxpm, folderopenxpmmask,
2360 gtk_ctree_expand(ctree, parent_node);
2361 gtk_ctree_node_set_row_data(ctree, node, item);
2363 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2364 folderview_sort_folders(folderview, parent_node, item->folder);
2366 hookdata.item = item;
2367 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2368 hookdata.msg = NULL;
2369 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2371 gtk_clist_thaw(GTK_CLIST(ctree));
2374 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2377 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2379 GSList *mlist = NULL;
2381 FolderItem *special_trash = NULL;
2384 if (!folderview->selected) return;
2385 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2386 g_return_if_fail(item != NULL);
2387 g_return_if_fail(item->folder != NULL);
2389 if (NULL != (ac = account_find_from_item(item)))
2390 special_trash = account_get_special_folder(ac, F_TRASH);
2392 if (item != item->folder->trash && item != special_trash
2393 && !folder_has_parent_of_type(item, F_TRASH)) return;
2395 if (prefs_common.ask_on_clean) {
2396 if (alertpanel(_("Empty trash"),
2397 _("Delete all messages in trash?"),
2398 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2402 mlist = folder_item_get_msg_list(item);
2404 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2405 MsgInfo * msginfo = (MsgInfo *) cur->data;
2406 if (MSG_IS_LOCKED(msginfo->flags))
2408 /* is it partially received? (partial_recv isn't cached) */
2409 if (msginfo->total_size != 0 &&
2410 msginfo->size != (off_t)msginfo->total_size)
2411 partial_mark_for_delete(msginfo);
2413 procmsg_msg_list_free(mlist);
2415 folder_item_remove_all_msg(item);
2418 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2421 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2423 FolderItem *special_queue = NULL;
2425 gchar *errstr = NULL;
2427 if (!folderview->selected) return;
2428 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2429 g_return_if_fail(item != NULL);
2430 g_return_if_fail(item->folder != NULL);
2432 if (NULL != (ac = account_find_from_item(item)))
2433 special_queue = account_get_special_folder(ac, F_QUEUE);
2435 if (item != item->folder->queue && item != special_queue
2436 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2438 if (procmsg_queue_is_empty(item))
2441 if (prefs_common.work_offline)
2442 if (alertpanel(_("Offline warning"),
2443 _("You're working offline. Override?"),
2444 GTK_STOCK_NO, GTK_STOCK_YES,
2445 NULL) != G_ALERTALTERNATE)
2448 /* ask for confirmation before sending queued messages only
2449 in online mode and if there is at least one message queued
2450 in any of the folder queue
2452 if (prefs_common.confirm_send_queued_messages) {
2453 if (!prefs_common.work_offline) {
2454 if (alertpanel(_("Send queued messages"),
2455 _("Send all queued messages?"),
2456 GTK_STOCK_CANCEL, _("_Send"),
2457 NULL) != G_ALERTALTERNATE)
2462 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2464 alertpanel_error_log(_("Some errors occurred while "
2465 "sending queued messages."));
2467 alertpanel_error_log(_("Some errors occurred "
2468 "while sending queued messages:\n%s"), errstr);
2474 static void folderview_search_cb(FolderView *folderview, guint action,
2477 summary_search(folderview->summaryview);
2480 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2483 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2486 if (!folderview->selected) return;
2488 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2489 g_return_if_fail(item != NULL);
2490 g_return_if_fail(item->folder != NULL);
2492 item->processing_pending = TRUE;
2493 folder_item_apply_processing(item);
2494 item->processing_pending = FALSE;
2497 static void folderview_property_cb(FolderView *folderview, guint action,
2500 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2503 if (!folderview->selected) return;
2505 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2506 g_return_if_fail(item != NULL);
2507 g_return_if_fail(item->folder != NULL);
2509 prefs_folder_item_open(item);
2512 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2514 GSList *list = NULL;
2515 GSList *done = NULL;
2516 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2518 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2519 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2520 && list->data != node) {
2521 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2522 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2525 for (list = done; list != NULL; list = g_slist_next(list)) {
2526 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2532 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2533 FolderItem *to_folder, gboolean copy)
2535 FolderItem *from_parent = NULL;
2536 FolderItem *new_folder = NULL;
2537 GtkCTreeNode *src_node = NULL;
2541 g_return_if_fail(folderview != NULL);
2542 g_return_if_fail(from_folder != NULL);
2543 g_return_if_fail(to_folder != NULL);
2545 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2546 from_parent = folder_item_parent(from_folder);
2548 if (prefs_common.warn_dnd) {
2549 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2550 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2551 from_folder->name, to_folder->name);
2552 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2553 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2554 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2557 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2559 else if (status & G_ALERTDISABLE)
2560 prefs_common.warn_dnd = FALSE;
2563 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2564 from_folder->name, to_folder->name);
2565 STATUSBAR_PUSH(folderview->mainwin, buf);
2567 summary_clear_all(folderview->summaryview);
2568 folderview->opened = NULL;
2569 folderview->selected = NULL;
2570 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2572 main_window_cursor_wait(folderview->mainwin);
2574 statusbar_verbosity_set(FALSE);
2575 folder_item_update_freeze();
2576 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2577 statusbar_verbosity_set(FALSE);
2578 main_window_cursor_normal(folderview->mainwin);
2579 STATUSBAR_POP(folderview->mainwin);
2580 folder_item_update_thaw();
2581 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2583 folderview_sort_folders(folderview,
2584 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2585 NULL, to_folder), new_folder->folder);
2586 folderview_select(folderview, new_folder);
2588 statusbar_verbosity_set(FALSE);
2589 main_window_cursor_normal(folderview->mainwin);
2590 STATUSBAR_POP(folderview->mainwin);
2591 folder_item_update_thaw();
2593 case F_MOVE_FAILED_DEST_IS_PARENT:
2594 alertpanel_error(_("Source and destination are the same."));
2596 case F_MOVE_FAILED_DEST_IS_CHILD:
2597 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2598 _("Can't move a folder to one of its children."));
2600 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2601 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2604 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2609 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2612 static gint folderview_clist_compare(GtkCList *clist,
2613 gconstpointer ptr1, gconstpointer ptr2)
2615 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2616 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2618 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2620 return item1->order - item2->order;
2623 // if only one folder has an order it comes first
2624 if (item1->order > 0)
2628 if (item2->order > 0)
2634 return (item2->name != NULL);
2638 return g_utf8_collate(item1->name, item2->name);
2641 static void folderview_processing_cb(FolderView *folderview, guint action,
2644 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2648 if (!folderview->selected) return;
2650 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2651 g_return_if_fail(item != NULL);
2652 g_return_if_fail(item->folder != NULL);
2654 id = folder_item_get_identifier(item);
2655 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2658 prefs_filtering_open(&item->prefs->processing, title,
2659 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2663 void folderview_set_target_folder_color(gint color_op)
2667 FolderView *folderview;
2669 for (list = folderview_list; list != NULL; list = list->next) {
2670 folderview = (FolderView *)list->data;
2671 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2673 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2674 folderview->color_op;
2680 static gchar *last_font = NULL;
2681 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2689 void folderview_reflect_prefs(void)
2691 gboolean update_font = TRUE;
2692 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2693 FolderItem *item = folderview_get_selected_item(folderview);
2694 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2695 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2696 gint height = pos->value;
2698 if (last_font && !strcmp(last_font, NORMAL_FONT))
2699 update_font = FALSE;
2702 last_font = g_strdup(NORMAL_FONT);
2705 normal_style = normal_color_style = bold_style =
2706 bold_color_style = bold_tgtfold_style = NULL;
2708 folderview_init(folderview);
2710 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2711 folderview_column_set_titles(folderview);
2712 folderview_set_all();
2714 g_signal_handlers_block_by_func
2715 (G_OBJECT(folderview->ctree),
2716 G_CALLBACK(folderview_selected), folderview);
2719 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2720 GTK_CTREE(folderview->ctree), NULL, item);
2722 folderview_select(folderview, item);
2723 folderview->open_folder = FALSE;
2724 folderview->selected = node;
2727 g_signal_handlers_unblock_by_func
2728 (G_OBJECT(folderview->ctree),
2729 G_CALLBACK(folderview_selected), folderview);
2731 pos = gtk_scrolled_window_get_vadjustment(
2732 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2733 gtk_adjustment_set_value(pos, height);
2734 gtk_adjustment_changed(pos);
2735 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2738 static void drag_state_stop(FolderView *folderview)
2740 if (folderview->drag_timer)
2741 g_source_remove(folderview->drag_timer);
2742 folderview->drag_timer = 0;
2743 folderview->drag_node = NULL;
2746 static gint folderview_defer_expand(FolderView *folderview)
2748 if (folderview->drag_node) {
2749 folderview_recollapse_nodes(folderview, folderview->drag_node);
2750 if (folderview->drag_item->collapsed) {
2751 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2752 folderview->nodes_to_recollapse = g_slist_append
2753 (folderview->nodes_to_recollapse, folderview->drag_node);
2756 folderview->drag_item = NULL;
2757 folderview->drag_timer = 0;
2761 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2763 /* the idea is that we call drag_state_start() whenever we want expansion to
2764 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2765 * we need to call drag_state_stop() */
2766 drag_state_stop(folderview);
2767 /* request expansion */
2768 if (0 != (folderview->drag_timer = g_timeout_add
2769 (prefs_common.hover_timeout,
2770 (GtkFunction)folderview_defer_expand,
2772 folderview->drag_node = node;
2773 folderview->drag_item = item;
2777 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2778 FolderView *folderview)
2780 GdkDragContext *context;
2782 g_return_if_fail(folderview != NULL);
2783 if (folderview->selected == NULL) return;
2784 if (folderview->nodes_to_recollapse)
2785 g_slist_free(folderview->nodes_to_recollapse);
2786 folderview->nodes_to_recollapse = NULL;
2787 context = gtk_drag_begin(widget, folderview->target_list,
2788 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2789 gtk_drag_set_icon_default(context);
2792 static void folderview_drag_data_get(GtkWidget *widget,
2793 GdkDragContext *drag_context,
2794 GtkSelectionData *selection_data,
2797 FolderView *folderview)
2801 gchar *source = NULL;
2802 if (info == TARGET_DUMMY) {
2803 for (cur = GTK_CLIST(folderview->ctree)->selection;
2804 cur != NULL; cur = cur->next) {
2805 item = gtk_ctree_node_get_row_data
2806 (GTK_CTREE(folderview->ctree),
2807 GTK_CTREE_NODE(cur->data));
2809 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2810 gtk_selection_data_set(selection_data,
2811 selection_data->target, 8,
2812 source, strlen(source));
2818 g_warning("unknown info %d\n", info);
2822 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2824 FolderUpdateData *hookdata;
2825 FolderView *folderview;
2829 folderview = (FolderView *) userdata;
2830 g_return_val_if_fail(hookdata != NULL, FALSE);
2831 g_return_val_if_fail(folderview != NULL, FALSE);
2833 ctree = folderview->ctree;
2834 g_return_val_if_fail(ctree != NULL, FALSE);
2836 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2837 folderview_create_folder_node(folderview, hookdata->item);
2838 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2839 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2840 NULL, folder_item_parent(hookdata->item));
2841 folderview_sort_folders(folderview, node, hookdata->folder);
2842 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2845 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2847 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2848 if (folderview->selected == node)
2849 folderview->selected = NULL;
2850 if (folderview->opened == node)
2851 folderview->opened = NULL;
2853 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2854 folderview_set(folderview);
2859 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2860 GdkDragContext *context,
2864 FolderView *folderview)
2867 FolderItem *item = NULL, *src_item = NULL;
2868 GtkCTreeNode *node = NULL;
2869 gboolean acceptable = FALSE;
2870 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2871 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2872 int height = (int)pos->page_size;
2873 int total_height = (int)pos->upper;
2874 int vpos = (int) pos->value;
2875 int offset = prefs_common.show_col_headers ? 24:0;
2877 if (gtk_clist_get_selection_info
2878 (GTK_CLIST(widget), x - offset, y - offset, &row, &column)) {
2879 GtkWidget *srcwidget;
2881 if (y > height - 24 && height + vpos < total_height) {
2882 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2883 gtk_adjustment_changed(pos);
2885 if (y < 48 && y > 0) {
2886 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2887 gtk_adjustment_changed(pos);
2890 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2891 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2892 src_item = folderview->summaryview->folder_item;
2894 srcwidget = gtk_drag_get_source_widget(context);
2895 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2896 /* comes from summaryview */
2897 /* we are copying messages, so only accept folder items that are not
2898 the source item, are no root items and can copy messages */
2899 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2900 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2901 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2903 } else if (srcwidget == folderview->ctree) {
2904 /* comes from folderview */
2905 /* we are moving folder items, only accept folders that are not
2906 the source items and can copy messages and create folder items */
2907 if (item && item->folder && src_item && src_item != item &&
2908 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2909 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2910 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2911 || item->folder == src_item->folder))
2914 /* comes from another app */
2915 /* we are adding messages, so only accept folder items that are
2916 no root items and can copy messages */
2917 if (item && item->folder && folder_item_parent(item) != NULL
2918 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2919 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2924 if (acceptable || (src_item && src_item == item))
2925 drag_state_start(folderview, node, item);
2928 g_signal_handlers_block_by_func
2930 G_CALLBACK(folderview_selected), folderview);
2931 gtk_ctree_select(GTK_CTREE(widget), node);
2932 g_signal_handlers_unblock_by_func
2934 G_CALLBACK(folderview_selected), folderview);
2935 gdk_drag_status(context,
2936 (context->actions == GDK_ACTION_COPY ?
2937 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2939 if (folderview->opened)
2940 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2941 gdk_drag_status(context, 0, time);
2947 static void folderview_drag_leave_cb(GtkWidget *widget,
2948 GdkDragContext *context,
2950 FolderView *folderview)
2952 drag_state_stop(folderview);
2953 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2956 static void free_info (gpointer stuff, gpointer data)
2961 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2962 guint time, FolderItem *item)
2965 GSList *msglist = NULL;
2966 list = uri_list_extract_filenames(data);
2967 if (!(item && item->folder && folder_item_parent(item) != NULL
2968 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2970 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2971 debug_print("item doesn't fit\n");
2975 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2976 debug_print("list is empty\n");
2979 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2980 MsgFileInfo *info = NULL;
2982 if (file_is_email((gchar *)tmp->data)) {
2983 info = g_new0(MsgFileInfo, 1);
2984 info->msginfo = NULL;
2985 info->file = (gchar *)tmp->data;
2986 msglist = g_slist_prepend(msglist, info);
2987 debug_print("file is a mail\n");
2989 debug_print("file isn't a mail\n");
2993 msglist = g_slist_reverse(msglist);
2994 folder_item_add_msgs(item, msglist, FALSE);
2995 g_slist_foreach(msglist, free_info, NULL);
2996 g_slist_free(msglist);
2997 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2999 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3001 list_free_strings(list);
3005 static void folderview_drag_received_cb(GtkWidget *widget,
3006 GdkDragContext *drag_context,
3009 GtkSelectionData *data,
3012 FolderView *folderview)
3015 FolderItem *item = NULL, *src_item;
3017 int offset = prefs_common.show_col_headers ? 24:0;
3019 if (info == TARGET_DUMMY) {
3020 drag_state_stop(folderview);
3021 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3022 /* comes from summaryview */
3023 if (gtk_clist_get_selection_info
3024 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3027 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3028 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3029 src_item = folderview->summaryview->folder_item;
3031 if (item->no_select) {
3032 alertpanel_error(_("The destination folder can only be used to "
3033 "store subfolders."));
3036 /* re-check (due to acceptable possibly set for folder moves */
3037 if (!(item && item->folder && item->path && !item->no_select &&
3038 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3041 if (item && src_item) {
3042 switch (drag_context->action) {
3043 case GDK_ACTION_COPY:
3044 summary_copy_selected_to(folderview->summaryview, item);
3045 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3047 case GDK_ACTION_MOVE:
3048 case GDK_ACTION_DEFAULT:
3050 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3051 summary_copy_selected_to(folderview->summaryview, item);
3053 summary_move_selected_to(folderview->summaryview, item);
3054 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3057 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3059 /* comes from folderview */
3061 gboolean folder_is_normal = TRUE;
3062 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3064 source = data->data + 17;
3065 if (gtk_clist_get_selection_info
3066 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0
3068 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3071 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3072 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3073 src_item = folder_find_item_from_identifier(source);
3077 src_item->stype == F_NORMAL &&
3078 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3079 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3080 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3081 !folder_has_parent_of_type(src_item, F_TRASH);
3082 if (!item || !src_item || !folder_is_normal) {
3083 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3087 folderview_move_folder(folderview, src_item, item, copy);
3088 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3090 folderview->nodes_to_recollapse = NULL;
3091 } else if (info == TARGET_MAIL_URI_LIST) {
3092 if (gtk_clist_get_selection_info
3093 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3096 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3098 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3099 debug_print("no node\n");
3102 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3104 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3105 debug_print("no item\n");
3108 folderview_finish_dnd(data->data, drag_context, time, item);
3112 static void folderview_drag_end_cb(GtkWidget *widget,
3113 GdkDragContext *drag_context,
3114 FolderView *folderview)
3116 drag_state_stop(folderview);
3117 g_slist_free(folderview->nodes_to_recollapse);
3118 folderview->nodes_to_recollapse = NULL;
3121 void folderview_register_popup(FolderViewPopup *fpopup)
3125 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3126 FolderView *folderview = folderviews->data;
3127 GtkItemFactory *factory;
3129 factory = create_ifactory(folderview, fpopup);
3130 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3132 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3135 void folderview_unregister_popup(FolderViewPopup *fpopup)
3139 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3140 FolderView *folderview = folderviews->data;
3142 g_hash_table_remove(folderview->popups, fpopup->klass);
3144 g_hash_table_remove(folderview_popups, fpopup->klass);