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);
1939 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1940 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1941 folderview->selected == folderview->opened);
1942 SET_SENS("/Run processing rules", item->prefs->processing &&
1943 item->total_msgs >= 1);
1944 SET_SENS("/Properties...", !item->no_select);
1945 SET_SENS("/Processing...", item->node->parent != NULL && !item->no_select);
1946 if (item == folder->trash || item == special_trash
1947 || folder_has_parent_of_type(item, F_TRASH)) {
1948 GSList *msglist = folder_item_get_msg_list(item);
1949 SET_SENS("/Empty trash...", msglist != NULL);
1950 procmsg_msg_list_free(msglist);
1952 if (item == folder->queue || item == special_queue
1953 || folder_has_parent_of_type(item, F_QUEUE)) {
1954 GSList *msglist = folder_item_get_msg_list(item);
1955 SET_SENS("/Send queue...", msglist != NULL);
1956 procmsg_msg_list_free(msglist);
1960 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1961 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1962 event->button, event->time);
1965 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1966 FolderView *folderview)
1968 GtkCList *clist = GTK_CLIST(ctree);
1969 gint prev_row = -1, row = -1, column = -1;
1971 if (!event) return FALSE;
1973 if (event->button == 1 || event->button == 2) {
1974 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1975 folderview->open_folder = TRUE;
1977 if (event->type == GDK_2BUTTON_PRESS) {
1978 if (clist->selection) {
1981 node = GTK_CTREE_NODE(clist->selection->data);
1983 gtk_ctree_toggle_expansion(
1986 folderview->open_folder = FALSE;
1993 if (event->button == 2 || event->button == 3) {
1995 if (clist->selection) {
1998 node = GTK_CTREE_NODE(clist->selection->data);
2000 prev_row = gtkut_ctree_get_nth_from_node
2001 (GTK_CTREE(ctree), node);
2004 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
2007 if (prev_row != row) {
2008 gtk_clist_unselect_all(clist);
2009 if (event->button == 2)
2010 folderview_select_node
2012 gtk_ctree_node_nth(GTK_CTREE(ctree),
2015 gtk_clist_select_row(clist, row, column);
2019 if (event->button != 3) return FALSE;
2021 folderview_set_sens_and_popup_menu(folderview, row, event);
2025 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2026 FolderView *folderview)
2028 int row = -1, column = -1;
2030 if (!event) return FALSE;
2032 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2035 if (event->button == 1 && folderview->open_folder == FALSE &&
2036 folderview->opened != NULL) {
2037 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2038 folderview->opened);
2039 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2045 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2046 FolderView *folderview)
2048 if (!event) return FALSE;
2050 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2053 switch (event->keyval) {
2059 if (folderview->selected) {
2060 folderview_select_node(folderview,
2061 folderview->selected);
2066 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2067 gtk_ctree_toggle_expansion(
2068 GTK_CTREE(folderview->ctree),
2069 folderview->selected);
2074 if (folderview->selected) {
2075 if (folderview->opened == folderview->selected &&
2076 (!folderview->summaryview->folder_item ||
2077 folderview->summaryview->folder_item->total_msgs == 0))
2078 folderview_select_next_unread(folderview, TRUE);
2080 folderview_select_node(folderview,
2081 folderview->selected);
2091 typedef struct _PostponedSelectData
2096 FolderView *folderview;
2097 } PostponedSelectData;
2099 static gboolean postpone_select(void *data)
2101 PostponedSelectData *psdata = (PostponedSelectData *)data;
2102 debug_print("trying again\n");
2103 psdata->folderview->open_folder = TRUE;
2104 main_window_cursor_normal(psdata->folderview->mainwin);
2105 STATUSBAR_POP(psdata->folderview->mainwin);
2106 folderview_selected(psdata->ctree, psdata->row,
2107 psdata->column, psdata->folderview);
2112 void folderview_close_opened(FolderView *folderview)
2114 if (folderview->opened) {
2115 FolderItem *olditem;
2117 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2118 folderview->opened);
2120 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2121 olditem->path ? olditem->path:olditem->name);
2122 /* will be null if we just moved the previously opened folder */
2123 STATUSBAR_PUSH(folderview->mainwin, buf);
2124 main_window_cursor_wait(folderview->mainwin);
2126 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2127 summary_show(folderview->summaryview, NULL);
2128 folder_item_close(olditem);
2129 main_window_cursor_normal(folderview->mainwin);
2130 STATUSBAR_POP(folderview->mainwin);
2134 if (folderview->opened &&
2135 !GTK_CTREE_ROW(folderview->opened)->children)
2136 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2138 folderview->opened = NULL;
2140 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2141 gint column, FolderView *folderview)
2143 static gboolean can_select = TRUE; /* exclusive lock */
2148 GtkCTreeNode *old_opened = folderview->opened;
2150 folderview->selected = row;
2152 debug_print("newly selected %p, opened %p\n", folderview->selected,
2153 folderview->opened);
2154 if (folderview->opened == row) {
2155 folderview->open_folder = FALSE;
2160 item = gtk_ctree_node_get_row_data(ctree, row);
2163 folderview->open_folder = FALSE;
2167 if (!can_select || summary_is_locked(folderview->summaryview)) {
2168 if (folderview->opened) {
2169 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2170 gtk_ctree_select(ctree, folderview->opened);
2172 folderview->open_folder = FALSE;
2177 if (!folderview->open_folder) {
2184 /* Save cache for old folder */
2185 /* We don't want to lose all caches if sylpheed crashed */
2186 /* resets folderview->opened to NULL */
2187 folderview_close_opened(folderview);
2189 /* CLAWS: set compose button type: news folder items
2190 * always have a news folder as parent */
2192 toolbar_set_compose_button
2193 (folderview->mainwin->toolbar,
2194 FOLDER_TYPE(item->folder) == F_NEWS ?
2195 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2198 debug_print("Folder %s is selected\n", item->path);
2200 if (!GTK_CTREE_ROW(row)->children)
2201 gtk_ctree_expand(ctree, row);
2203 /* ungrab the mouse event */
2204 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2205 gtk_grab_remove(GTK_WIDGET(ctree));
2206 if (gdk_pointer_is_grabbed())
2207 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2211 /* TODO: wwp: avoid displaying (null) in the status bar */
2212 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2213 item->path : "(null)");
2214 debug_print("%s\n", buf);
2215 STATUSBAR_PUSH(folderview->mainwin, buf);
2218 main_window_cursor_wait(folderview->mainwin);
2220 if (folderview->scanning_folder == item->folder) {
2223 res = folder_item_open(item);
2226 if (res == -1 && item->no_select == FALSE) {
2227 main_window_cursor_normal(folderview->mainwin);
2228 STATUSBAR_POP(folderview->mainwin);
2230 alertpanel_error(_("Folder could not be opened."));
2232 folderview->open_folder = FALSE;
2236 } else if (res == -2 && item->no_select == FALSE) {
2237 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2238 data->ctree = ctree;
2240 data->column = column;
2241 data->folderview = folderview;
2242 debug_print("postponing open of %s till end of scan\n",
2243 item->path ? item->path:item->name);
2244 folderview->open_folder = FALSE;
2246 g_timeout_add(500, postpone_select, data);
2251 main_window_cursor_normal(folderview->mainwin);
2254 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2255 opened = summary_show(folderview->summaryview, item);
2257 folder_clean_cache_memory(item);
2260 gtkut_ctree_set_focus_row(ctree, old_opened);
2261 gtk_ctree_select(ctree, old_opened);
2262 folderview->opened = old_opened;
2264 folderview->opened = row;
2265 if (gtk_ctree_node_is_visible(ctree, row)
2266 != GTK_VISIBILITY_FULL)
2267 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2270 STATUSBAR_POP(folderview->mainwin);
2272 folderview->open_folder = FALSE;
2277 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2278 FolderView *folderview)
2282 item = gtk_ctree_node_get_row_data(ctree, node);
2283 g_return_if_fail(item != NULL);
2284 item->collapsed = FALSE;
2285 folderview_update_node(folderview, node);
2288 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2289 FolderView *folderview)
2293 item = gtk_ctree_node_get_row_data(ctree, node);
2294 g_return_if_fail(item != NULL);
2295 item->collapsed = TRUE;
2296 folderview_update_node(folderview, node);
2299 static void folderview_popup_close(GtkMenuShell *menu_shell,
2300 FolderView *folderview)
2302 if (!folderview->opened) return;
2304 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2307 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2308 FolderView *folderview)
2310 FolderColumnType type = folderview->col_state[column].type;
2312 prefs_common.folder_col_size[type] = width;
2315 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2319 folderview_create_folder_node(folderview, item);
2321 if (!item || !item->folder || !item->folder->node)
2324 srcnode = item->folder->node;
2325 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2326 srcnode = srcnode->children;
2327 while (srcnode != NULL) {
2328 if (srcnode && srcnode->data) {
2329 FolderItem *next_item = (FolderItem*) srcnode->data;
2330 folderview_create_folder_node_recursive(folderview, next_item);
2332 srcnode = srcnode->next;
2336 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2338 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2339 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2340 GtkCTreeNode *node, *parent_node;
2341 gint *col_pos = folderview->col_pos;
2342 FolderItemUpdateData hookdata;
2344 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2345 if (parent_node == NULL)
2348 gtk_clist_freeze(GTK_CLIST(ctree));
2350 text[col_pos[F_COL_FOLDER]] = item->name;
2351 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2353 folderxpm, folderxpmmask,
2354 folderopenxpm, folderopenxpmmask,
2356 gtk_ctree_expand(ctree, parent_node);
2357 gtk_ctree_node_set_row_data(ctree, node, item);
2359 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2360 folderview_sort_folders(folderview, parent_node, item->folder);
2362 hookdata.item = item;
2363 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2364 hookdata.msg = NULL;
2365 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2367 gtk_clist_thaw(GTK_CLIST(ctree));
2370 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2373 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2375 GSList *mlist = NULL;
2377 FolderItem *special_trash = NULL;
2380 if (!folderview->selected) return;
2381 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2382 g_return_if_fail(item != NULL);
2383 g_return_if_fail(item->folder != NULL);
2385 if (NULL != (ac = account_find_from_item(item)))
2386 special_trash = account_get_special_folder(ac, F_TRASH);
2388 if (item != item->folder->trash && item != special_trash
2389 && !folder_has_parent_of_type(item, F_TRASH)) return;
2391 if (prefs_common.ask_on_clean) {
2392 if (alertpanel(_("Empty trash"),
2393 _("Delete all messages in trash?"),
2394 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2398 mlist = folder_item_get_msg_list(item);
2400 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2401 MsgInfo * msginfo = (MsgInfo *) cur->data;
2402 if (MSG_IS_LOCKED(msginfo->flags))
2404 /* is it partially received? (partial_recv isn't cached) */
2405 if (msginfo->total_size != 0 &&
2406 msginfo->size != (off_t)msginfo->total_size)
2407 partial_mark_for_delete(msginfo);
2409 procmsg_msg_list_free(mlist);
2411 folder_item_remove_all_msg(item);
2414 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2417 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2419 FolderItem *special_queue = NULL;
2421 gchar *errstr = NULL;
2423 if (!folderview->selected) return;
2424 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2425 g_return_if_fail(item != NULL);
2426 g_return_if_fail(item->folder != NULL);
2428 if (NULL != (ac = account_find_from_item(item)))
2429 special_queue = account_get_special_folder(ac, F_QUEUE);
2431 if (item != item->folder->queue && item != special_queue
2432 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2434 if (procmsg_queue_is_empty(item))
2437 if (prefs_common.work_offline)
2438 if (alertpanel(_("Offline warning"),
2439 _("You're working offline. Override?"),
2440 GTK_STOCK_NO, GTK_STOCK_YES,
2441 NULL) != G_ALERTALTERNATE)
2444 /* ask for confirmation before sending queued messages only
2445 in online mode and if there is at least one message queued
2446 in any of the folder queue
2448 if (prefs_common.confirm_send_queued_messages) {
2449 if (!prefs_common.work_offline) {
2450 if (alertpanel(_("Send queued messages"),
2451 _("Send all queued messages?"),
2452 GTK_STOCK_CANCEL, _("_Send"),
2453 NULL) != G_ALERTALTERNATE)
2458 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2460 alertpanel_error_log(_("Some errors occurred while "
2461 "sending queued messages."));
2463 alertpanel_error_log(_("Some errors occurred "
2464 "while sending queued messages:\n%s"), errstr);
2470 static void folderview_search_cb(FolderView *folderview, guint action,
2473 summary_search(folderview->summaryview);
2476 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2479 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2482 if (!folderview->selected) return;
2484 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2485 g_return_if_fail(item != NULL);
2486 g_return_if_fail(item->folder != NULL);
2488 folder_item_apply_processing(item);
2491 static void folderview_property_cb(FolderView *folderview, guint action,
2494 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2497 if (!folderview->selected) return;
2499 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2500 g_return_if_fail(item != NULL);
2501 g_return_if_fail(item->folder != NULL);
2503 prefs_folder_item_open(item);
2506 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2508 GSList *list = NULL;
2509 GSList *done = NULL;
2510 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2512 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2513 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2514 && list->data != node) {
2515 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2516 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2519 for (list = done; list != NULL; list = g_slist_next(list)) {
2520 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2526 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2527 FolderItem *to_folder, gboolean copy)
2529 FolderItem *from_parent = NULL;
2530 FolderItem *new_folder = NULL;
2531 GtkCTreeNode *src_node = NULL;
2535 g_return_if_fail(folderview != NULL);
2536 g_return_if_fail(from_folder != NULL);
2537 g_return_if_fail(to_folder != NULL);
2539 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2540 from_parent = folder_item_parent(from_folder);
2542 if (prefs_common.warn_dnd) {
2543 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2544 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2545 from_folder->name, to_folder->name);
2546 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2547 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2548 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2551 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2553 else if (status & G_ALERTDISABLE)
2554 prefs_common.warn_dnd = FALSE;
2557 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2558 from_folder->name, to_folder->name);
2559 STATUSBAR_PUSH(folderview->mainwin, buf);
2561 summary_clear_all(folderview->summaryview);
2562 folderview->opened = NULL;
2563 folderview->selected = NULL;
2564 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2566 main_window_cursor_wait(folderview->mainwin);
2568 statusbar_verbosity_set(FALSE);
2569 folder_item_update_freeze();
2570 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2571 statusbar_verbosity_set(FALSE);
2572 main_window_cursor_normal(folderview->mainwin);
2573 STATUSBAR_POP(folderview->mainwin);
2574 folder_item_update_thaw();
2575 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2577 folderview_sort_folders(folderview,
2578 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2579 NULL, to_folder), new_folder->folder);
2580 folderview_select(folderview, new_folder);
2582 statusbar_verbosity_set(FALSE);
2583 main_window_cursor_normal(folderview->mainwin);
2584 STATUSBAR_POP(folderview->mainwin);
2585 folder_item_update_thaw();
2587 case F_MOVE_FAILED_DEST_IS_PARENT:
2588 alertpanel_error(_("Source and destination are the same."));
2590 case F_MOVE_FAILED_DEST_IS_CHILD:
2591 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2592 _("Can't move a folder to one of its children."));
2594 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2595 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2598 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2603 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2606 static gint folderview_clist_compare(GtkCList *clist,
2607 gconstpointer ptr1, gconstpointer ptr2)
2609 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2610 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2612 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2614 return item1->order - item2->order;
2617 // if only one folder has an order it comes first
2618 if (item1->order > 0)
2622 if (item2->order > 0)
2628 return (item2->name != NULL);
2632 return g_utf8_collate(item1->name, item2->name);
2635 static void folderview_processing_cb(FolderView *folderview, guint action,
2638 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2642 if (!folderview->selected) return;
2644 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2645 g_return_if_fail(item != NULL);
2646 g_return_if_fail(item->folder != NULL);
2648 id = folder_item_get_identifier(item);
2649 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2652 prefs_filtering_open(&item->prefs->processing, title,
2653 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2657 void folderview_set_target_folder_color(gint color_op)
2661 FolderView *folderview;
2663 for (list = folderview_list; list != NULL; list = list->next) {
2664 folderview = (FolderView *)list->data;
2665 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2667 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2668 folderview->color_op;
2674 static gchar *last_font = NULL;
2675 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2683 void folderview_reflect_prefs(void)
2685 gboolean update_font = TRUE;
2686 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2687 FolderItem *item = folderview_get_selected_item(folderview);
2688 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2689 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2690 gint height = pos->value;
2692 if (last_font && !strcmp(last_font, NORMAL_FONT))
2693 update_font = FALSE;
2696 last_font = g_strdup(NORMAL_FONT);
2699 normal_style = normal_color_style = bold_style =
2700 bold_color_style = bold_tgtfold_style = NULL;
2702 folderview_init(folderview);
2704 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2705 folderview_column_set_titles(folderview);
2706 folderview_set_all();
2708 g_signal_handlers_block_by_func
2709 (G_OBJECT(folderview->ctree),
2710 G_CALLBACK(folderview_selected), folderview);
2713 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2714 GTK_CTREE(folderview->ctree), NULL, item);
2716 folderview_select(folderview, item);
2717 folderview->open_folder = FALSE;
2718 folderview->selected = node;
2721 g_signal_handlers_unblock_by_func
2722 (G_OBJECT(folderview->ctree),
2723 G_CALLBACK(folderview_selected), folderview);
2725 pos = gtk_scrolled_window_get_vadjustment(
2726 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2727 gtk_adjustment_set_value(pos, height);
2728 gtk_adjustment_changed(pos);
2729 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2732 static void drag_state_stop(FolderView *folderview)
2734 if (folderview->drag_timer)
2735 g_source_remove(folderview->drag_timer);
2736 folderview->drag_timer = 0;
2737 folderview->drag_node = NULL;
2740 static gint folderview_defer_expand(FolderView *folderview)
2742 if (folderview->drag_node) {
2743 folderview_recollapse_nodes(folderview, folderview->drag_node);
2744 if (folderview->drag_item->collapsed) {
2745 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2746 folderview->nodes_to_recollapse = g_slist_append
2747 (folderview->nodes_to_recollapse, folderview->drag_node);
2750 folderview->drag_item = NULL;
2751 folderview->drag_timer = 0;
2755 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2757 /* the idea is that we call drag_state_start() whenever we want expansion to
2758 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2759 * we need to call drag_state_stop() */
2760 drag_state_stop(folderview);
2761 /* request expansion */
2762 if (0 != (folderview->drag_timer = g_timeout_add
2763 (prefs_common.hover_timeout,
2764 (GtkFunction)folderview_defer_expand,
2766 folderview->drag_node = node;
2767 folderview->drag_item = item;
2771 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2772 FolderView *folderview)
2774 GdkDragContext *context;
2776 g_return_if_fail(folderview != NULL);
2777 if (folderview->selected == NULL) return;
2778 if (folderview->nodes_to_recollapse)
2779 g_slist_free(folderview->nodes_to_recollapse);
2780 folderview->nodes_to_recollapse = NULL;
2781 context = gtk_drag_begin(widget, folderview->target_list,
2782 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2783 gtk_drag_set_icon_default(context);
2786 static void folderview_drag_data_get(GtkWidget *widget,
2787 GdkDragContext *drag_context,
2788 GtkSelectionData *selection_data,
2791 FolderView *folderview)
2795 gchar *source = NULL;
2796 if (info == TARGET_DUMMY) {
2797 for (cur = GTK_CLIST(folderview->ctree)->selection;
2798 cur != NULL; cur = cur->next) {
2799 item = gtk_ctree_node_get_row_data
2800 (GTK_CTREE(folderview->ctree),
2801 GTK_CTREE_NODE(cur->data));
2803 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2804 gtk_selection_data_set(selection_data,
2805 selection_data->target, 8,
2806 source, strlen(source));
2812 g_warning("unknown info %d\n", info);
2816 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2818 FolderUpdateData *hookdata;
2819 FolderView *folderview;
2823 folderview = (FolderView *) userdata;
2824 g_return_val_if_fail(hookdata != NULL, FALSE);
2825 g_return_val_if_fail(folderview != NULL, FALSE);
2827 ctree = folderview->ctree;
2828 g_return_val_if_fail(ctree != NULL, FALSE);
2830 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2831 folderview_create_folder_node(folderview, hookdata->item);
2832 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2833 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2834 NULL, folder_item_parent(hookdata->item));
2835 folderview_sort_folders(folderview, node, hookdata->folder);
2836 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2839 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2841 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2842 if (folderview->selected == node)
2843 folderview->selected = NULL;
2844 if (folderview->opened == node)
2845 folderview->opened = NULL;
2847 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2848 folderview_set(folderview);
2853 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2854 GdkDragContext *context,
2858 FolderView *folderview)
2861 FolderItem *item = NULL, *src_item = NULL;
2862 GtkCTreeNode *node = NULL;
2863 gboolean acceptable = FALSE;
2864 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2865 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2866 int height = (int)pos->page_size;
2867 int total_height = (int)pos->upper;
2868 int vpos = (int) pos->value;
2869 int offset = prefs_common.show_col_headers ? 24:0;
2871 if (gtk_clist_get_selection_info
2872 (GTK_CLIST(widget), x - offset, y - offset, &row, &column)) {
2873 GtkWidget *srcwidget;
2875 if (y > height - 24 && height + vpos < total_height) {
2876 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2877 gtk_adjustment_changed(pos);
2879 if (y < 48 && y > 0) {
2880 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2881 gtk_adjustment_changed(pos);
2884 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2885 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2886 src_item = folderview->summaryview->folder_item;
2888 srcwidget = gtk_drag_get_source_widget(context);
2889 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2890 /* comes from summaryview */
2891 /* we are copying messages, so only accept folder items that are not
2892 the source item, are no root items and can copy messages */
2893 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2894 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2895 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2897 } else if (srcwidget == folderview->ctree) {
2898 /* comes from folderview */
2899 /* we are moving folder items, only accept folders that are not
2900 the source items and can copy messages and create folder items */
2901 if (item && item->folder && src_item && src_item != item &&
2902 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2903 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2904 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2905 || item->folder == src_item->folder))
2908 /* comes from another app */
2909 /* we are adding messages, so only accept folder items that are
2910 no root items and can copy messages */
2911 if (item && item->folder && folder_item_parent(item) != NULL
2912 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2913 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2918 if (acceptable || (src_item && src_item == item))
2919 drag_state_start(folderview, node, item);
2922 g_signal_handlers_block_by_func
2924 G_CALLBACK(folderview_selected), folderview);
2925 gtk_ctree_select(GTK_CTREE(widget), node);
2926 g_signal_handlers_unblock_by_func
2928 G_CALLBACK(folderview_selected), folderview);
2929 gdk_drag_status(context,
2930 (context->actions == GDK_ACTION_COPY ?
2931 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2933 if (folderview->opened)
2934 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2935 gdk_drag_status(context, 0, time);
2941 static void folderview_drag_leave_cb(GtkWidget *widget,
2942 GdkDragContext *context,
2944 FolderView *folderview)
2946 drag_state_stop(folderview);
2947 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2950 static void free_info (gpointer stuff, gpointer data)
2955 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2956 guint time, FolderItem *item)
2959 GSList *msglist = NULL;
2960 list = uri_list_extract_filenames(data);
2961 if (!(item && item->folder && folder_item_parent(item) != NULL
2962 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2964 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2965 debug_print("item doesn't fit\n");
2969 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2970 debug_print("list is empty\n");
2973 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2974 MsgFileInfo *info = NULL;
2976 if (file_is_email((gchar *)tmp->data)) {
2977 info = g_new0(MsgFileInfo, 1);
2978 info->msginfo = NULL;
2979 info->file = (gchar *)tmp->data;
2980 msglist = g_slist_prepend(msglist, info);
2981 debug_print("file is a mail\n");
2983 debug_print("file isn't a mail\n");
2987 msglist = g_slist_reverse(msglist);
2988 folder_item_add_msgs(item, msglist, FALSE);
2989 g_slist_foreach(msglist, free_info, NULL);
2990 g_slist_free(msglist);
2991 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2993 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2995 list_free_strings(list);
2999 static void folderview_drag_received_cb(GtkWidget *widget,
3000 GdkDragContext *drag_context,
3003 GtkSelectionData *data,
3006 FolderView *folderview)
3009 FolderItem *item = NULL, *src_item;
3011 int offset = prefs_common.show_col_headers ? 24:0;
3013 if (info == TARGET_DUMMY) {
3014 drag_state_stop(folderview);
3015 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3016 /* comes from summaryview */
3017 if (gtk_clist_get_selection_info
3018 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3021 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3022 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3023 src_item = folderview->summaryview->folder_item;
3025 if (item->no_select) {
3026 alertpanel_error(_("The destination folder can only be used to "
3027 "store subfolders."));
3030 /* re-check (due to acceptable possibly set for folder moves */
3031 if (!(item && item->folder && item->path && !item->no_select &&
3032 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3035 if (item && src_item) {
3036 switch (drag_context->action) {
3037 case GDK_ACTION_COPY:
3038 summary_copy_selected_to(folderview->summaryview, item);
3039 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3041 case GDK_ACTION_MOVE:
3042 case GDK_ACTION_DEFAULT:
3044 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3045 summary_copy_selected_to(folderview->summaryview, item);
3047 summary_move_selected_to(folderview->summaryview, item);
3048 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3051 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3053 /* comes from folderview */
3055 gboolean folder_is_normal = TRUE;
3056 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3058 source = data->data + 17;
3059 if (gtk_clist_get_selection_info
3060 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0
3062 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3065 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3066 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3067 src_item = folder_find_item_from_identifier(source);
3071 src_item->stype == F_NORMAL &&
3072 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3073 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3074 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3075 !folder_has_parent_of_type(src_item, F_TRASH);
3076 if (!item || !src_item || !folder_is_normal) {
3077 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3081 folderview_move_folder(folderview, src_item, item, copy);
3082 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3084 folderview->nodes_to_recollapse = NULL;
3085 } else if (info == TARGET_MAIL_URI_LIST) {
3086 if (gtk_clist_get_selection_info
3087 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3090 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3092 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3093 debug_print("no node\n");
3096 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3098 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3099 debug_print("no item\n");
3102 folderview_finish_dnd(data->data, drag_context, time, item);
3106 static void folderview_drag_end_cb(GtkWidget *widget,
3107 GdkDragContext *drag_context,
3108 FolderView *folderview)
3110 drag_state_stop(folderview);
3111 g_slist_free(folderview->nodes_to_recollapse);
3112 folderview->nodes_to_recollapse = NULL;
3115 void folderview_register_popup(FolderViewPopup *fpopup)
3119 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3120 FolderView *folderview = folderviews->data;
3121 GtkItemFactory *factory;
3123 factory = create_ifactory(folderview, fpopup);
3124 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3126 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3129 void folderview_unregister_popup(FolderViewPopup *fpopup)
3133 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3134 FolderView *folderview = folderviews->data;
3136 g_hash_table_remove(folderview->popups, fpopup->klass);
3138 g_hash_table_remove(folderview_popups, fpopup->klass);