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;
860 folder_item_update_freeze();
861 if (folderview->summaryview->folder_item != item)
862 summary_lock(folderview->summaryview);
864 summary_freeze(folderview->summaryview);
866 folderutils_mark_all_read(item);
868 if (folderview->summaryview->folder_item != item)
869 summary_unlock(folderview->summaryview);
871 summary_thaw(folderview->summaryview);
872 folder_item_update_thaw();
875 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
877 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
879 g_return_if_fail(node != NULL);
881 if (folderview->open_folder) {
885 folderview->open_folder = TRUE;
886 gtkut_ctree_set_focus_row(ctree, node);
887 gtk_ctree_select(ctree, node);
888 if (folderview->summaryview->folder_item &&
889 folderview->summaryview->folder_item->total_msgs > 0)
890 summary_grab_focus(folderview->summaryview);
892 gtk_widget_grab_focus(folderview->ctree);
894 gtkut_ctree_expand_parent_all(ctree, node);
897 void folderview_unselect(FolderView *folderview)
899 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
901 (GTK_CTREE(folderview->ctree), folderview->opened);
903 folderview->selected = folderview->opened = NULL;
906 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
912 node = gtkut_ctree_node_next(ctree, node);
914 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
916 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
917 item = gtk_ctree_node_get_row_data(ctree, node);
918 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
925 void folderview_select_next_marked(FolderView *folderview)
927 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
928 GtkCTreeNode *node = NULL;
929 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
930 gboolean last_open = prefs_common.always_show_msg;
932 prefs_common.summary_select_prio[0] = ACTION_MARKED;
933 prefs_common.always_show_msg = TRUE;
935 if ((node = folderview_find_next_marked(ctree, folderview->opened))
937 folderview_select_node(folderview, node);
941 if (!folderview->opened ||
942 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
945 /* search again from the first node */
946 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
947 folderview_select_node(folderview, node);
950 prefs_common.summary_select_prio[0] = last_summary_select_prio;
951 prefs_common.always_show_msg = last_open;
954 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
960 node = gtkut_ctree_node_next(ctree, node);
962 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
964 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
965 item = gtk_ctree_node_get_row_data(ctree, node);
966 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
973 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
975 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
976 GtkCTreeNode *node = NULL;
977 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
978 gboolean last_open = prefs_common.always_show_msg;
980 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
981 prefs_common.always_show_msg = force_open ? TRUE : last_open;
983 if ((node = folderview_find_next_unread(ctree, folderview->opened))
985 folderview_select_node(folderview, node);
989 if (!folderview->opened ||
990 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
993 /* search again from the first node */
994 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
995 folderview_select_node(folderview, node);
998 prefs_common.summary_select_prio[0] = last_summary_select_prio;
999 prefs_common.always_show_msg = last_open;
1002 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
1008 node = gtkut_ctree_node_next(ctree, node);
1010 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1012 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1013 item = gtk_ctree_node_get_row_data(ctree, node);
1014 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
1021 void folderview_select_next_new(FolderView *folderview)
1023 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1024 GtkCTreeNode *node = NULL;
1025 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1026 gboolean last_open = prefs_common.always_show_msg;
1028 prefs_common.summary_select_prio[0] = ACTION_NEW;
1029 prefs_common.always_show_msg = TRUE;
1031 if ((node = folderview_find_next_new(ctree, folderview->opened))
1033 folderview_select_node(folderview, node);
1037 if (!folderview->opened ||
1038 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1041 /* search again from the first node */
1042 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1043 folderview_select_node(folderview, node);
1046 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1047 prefs_common.always_show_msg = last_open;
1050 FolderItem *folderview_get_selected_item(FolderView *folderview)
1052 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1054 if (!folderview->selected) return NULL;
1055 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1058 static void folderview_set_folders(FolderView *folderview)
1061 list = folder_get_list();
1063 for (; list != NULL; list = list->next) {
1064 folderview_append_folder(folderview, FOLDER(list->data));
1068 static gchar *get_scan_str(FolderItem *item)
1071 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1072 item->folder->name, G_DIR_SEPARATOR,
1075 return g_strdup_printf(_("Scanning folder %s ..."),
1076 item->folder->name);
1078 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1082 for (list = folderview_list; list != NULL; list = list->next) {
1083 FolderView *folderview = (FolderView *)list->data;
1084 MainWindow *mainwin = folderview->mainwin;
1085 gchar *str = get_scan_str(item);
1087 STATUSBAR_PUSH(mainwin, str);
1088 STATUSBAR_POP(mainwin);
1093 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1096 MainWindow *mainwin = mainwindow_get_mainwindow();
1097 FolderView *folderview = NULL;
1098 GtkAdjustment *pos = NULL;
1101 g_return_if_fail(folder != NULL);
1103 if (!folder->klass->scan_tree) return;
1106 alertpanel_full(_("Rebuild folder tree"),
1107 _("Rebuilding the folder tree will remove "
1108 "local caches. Do you want to continue?"),
1109 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1110 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1111 != G_ALERTALTERNATE) {
1117 window = label_window_create(_("Rebuilding folder tree..."));
1119 window = label_window_create(_("Scanning folder tree..."));
1122 folderview = mainwin->folderview;
1125 pos = gtk_scrolled_window_get_vadjustment(
1126 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1127 height = pos->value;
1130 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1131 folder_scan_tree(folder, rebuild);
1132 folder_set_ui_func(folder, NULL, NULL);
1134 folderview_set_all();
1137 pos = gtk_scrolled_window_get_vadjustment(
1138 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1139 gtk_adjustment_set_value(pos, height);
1140 gtk_adjustment_changed(pos);
1142 label_window_destroy(window);
1146 void folderview_fast_rescan_tree(Folder *folder)
1149 MainWindow *mainwin = mainwindow_get_mainwindow();
1150 FolderView *folderview = NULL;
1151 GtkAdjustment *pos = NULL;
1154 g_return_if_fail(folder != NULL);
1156 if (!folder->klass->scan_tree) return;
1160 window = label_window_create(_("Scanning folder tree..."));
1163 folderview = mainwin->folderview;
1166 pos = gtk_scrolled_window_get_vadjustment(
1167 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1168 height = pos->value;
1171 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1172 folder_fast_scan_tree(folder);
1173 folder_set_ui_func(folder, NULL, NULL);
1175 folderview_set_all();
1178 pos = gtk_scrolled_window_get_vadjustment(
1179 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1180 gtk_adjustment_set_value(pos, height);
1181 gtk_adjustment_changed(pos);
1183 label_window_destroy(window);
1187 /** folderview_check_new()
1188 * Scan and update the folder and return the
1189 * count the number of new messages since last check.
1190 * \param folder the folder to check for new messages
1191 * \return the number of new messages since last check
1193 gint folderview_check_new(Folder *folder)
1197 FolderView *folderview;
1201 gint former_new_msgs = 0;
1202 gint former_new = 0, former_unread = 0, former_total;
1204 for (list = folderview_list; list != NULL; list = list->next) {
1205 folderview = (FolderView *)list->data;
1206 ctree = GTK_CTREE(folderview->ctree);
1207 folderview->scanning_folder = folder;
1209 main_window_lock(folderview->mainwin);
1211 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1212 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1214 item = gtk_ctree_node_get_row_data(ctree, node);
1215 if (!item || !item->path || !item->folder) continue;
1216 if (item->no_select) continue;
1217 if (folder && folder != item->folder) continue;
1218 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1219 if (!item->prefs->newmailcheck) continue;
1220 if (item->processing_pending == TRUE) {
1221 debug_print("skipping %s, processing pending\n",
1222 item->path ? item->path : item->name);
1225 if (item->scanning != ITEM_NOT_SCANNING) {
1226 debug_print("skipping %s, scanning\n",
1227 item->path ? item->path : item->name);
1231 str = get_scan_str(item);
1233 STATUSBAR_PUSH(folderview->mainwin, str);
1237 folderview_scan_tree_func(item->folder, item, NULL);
1238 former_new = item->new_msgs;
1239 former_unread = item->unread_msgs;
1240 former_total = item->total_msgs;
1242 if (item->folder->klass->scan_required &&
1243 (item->folder->klass->scan_required(item->folder, item) ||
1244 item->folder->inbox == item ||
1245 item->opened == TRUE ||
1246 item->processing_pending == TRUE)) {
1247 if (folder_item_scan(item) < 0) {
1249 summaryview_unlock(folderview->summaryview, item);
1250 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1251 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1252 item->path ? item->path:item->name);
1253 STATUSBAR_POP(folderview->mainwin);
1255 } else if (!FOLDER_IS_LOCAL(folder)) {
1256 STATUSBAR_POP(folderview->mainwin);
1261 } else if (!item->folder->klass->scan_required) {
1262 if (folder_item_scan(item) < 0) {
1263 summaryview_unlock(folderview->summaryview, item);
1264 if (folder && !FOLDER_IS_LOCAL(folder)) {
1265 STATUSBAR_POP(folderview->mainwin);
1270 if (former_new != item->new_msgs ||
1271 former_unread != item->unread_msgs ||
1272 former_total != item->total_msgs)
1273 folderview_update_node(folderview, node);
1275 new_msgs += item->new_msgs;
1276 former_new_msgs += former_new;
1277 STATUSBAR_POP(folderview->mainwin);
1279 folderview->scanning_folder = NULL;
1280 main_window_unlock(folderview->mainwin);
1284 folder_write_list();
1285 /* Number of new messages since last check is the just the difference
1286 * between former_new_msgs and new_msgs. If new_msgs is less than
1287 * former_new_msgs, that would mean another session accessed the folder
1288 * and the result is not well defined.
1290 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1294 void folderview_check_new_all(void)
1298 FolderView *folderview;
1300 folderview = (FolderView *)folderview_list->data;
1303 main_window_lock(folderview->mainwin);
1304 window = label_window_create
1305 (_("Checking for new messages in all folders..."));
1307 list = folder_get_list();
1308 for (; list != NULL; list = list->next) {
1309 Folder *folder = list->data;
1311 folderview_check_new(folder);
1314 folder_write_list();
1315 folderview_set_all();
1317 label_window_destroy(window);
1318 main_window_unlock(folderview->mainwin);
1322 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1328 if (!item || !item->folder || !item->folder->node)
1331 node = item->folder->node;
1333 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1334 node = node->children;
1337 (item->new_msgs > 0 ||
1338 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1342 while (node != NULL) {
1343 if (node && node->data) {
1344 FolderItem *next_item = (FolderItem*) node->data;
1346 if (folderview_have_new_children_sub(folderview,
1355 static gboolean folderview_have_new_children(FolderView *folderview,
1358 return folderview_have_new_children_sub(folderview, item, FALSE);
1361 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1367 if (!item || !item->folder || !item->folder->node)
1370 node = item->folder->node;
1372 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1373 node = node->children;
1376 (item->unread_msgs > 0 ||
1377 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1381 while (node != NULL) {
1382 if (node && node->data) {
1383 FolderItem *next_item = (FolderItem*) node->data;
1385 if (folderview_have_unread_children_sub(folderview,
1395 static gboolean folderview_have_unread_children(FolderView *folderview,
1398 return folderview_have_unread_children_sub(folderview, item, FALSE);
1401 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1407 if (!item || !item->folder || !item->folder->node)
1410 node = item->folder->node;
1412 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1413 node = node->children;
1415 if (in_sub && item->search_match){
1419 while (node != NULL) {
1420 if (node && node->data) {
1421 FolderItem *next_item = (FolderItem*) node->data;
1423 if (folderview_have_matching_children_sub(folderview,
1433 static gboolean folderview_have_matching_children(FolderView *folderview,
1436 return folderview_have_matching_children_sub(folderview, item, FALSE);
1439 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1445 if (!item || !item->folder || !item->folder->node)
1448 node = item->folder->node;
1450 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1451 node = node->children;
1453 if (item->marked_msgs != 0) {
1457 while (node != NULL) {
1458 if (node && node->data) {
1459 FolderItem *next_item = (FolderItem*) node->data;
1461 if (folderview_have_marked_children_sub(folderview,
1470 static gboolean folderview_have_marked_children(FolderView *folderview,
1473 return folderview_have_marked_children_sub(folderview, item, FALSE);
1476 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1478 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1479 GtkStyle *style = NULL;
1480 GtkStyle *color_style = NULL;
1482 GdkPixmap *xpm, *openxpm;
1483 GdkBitmap *mask, *openmask;
1484 static GdkPixmap *searchicon;
1485 static GdkBitmap *searchmask;
1486 gboolean mark = FALSE;
1489 gboolean add_unread_mark;
1490 gboolean add_sub_match_mark;
1491 gboolean use_bold, use_color;
1492 gint *col_pos = folderview->col_pos;
1493 SpecialFolderItemType stype;
1495 item = gtk_ctree_node_get_row_data(ctree, node);
1496 g_return_if_fail(item != NULL);
1498 if (!GTK_CTREE_ROW(node)->expanded)
1499 mark = folderview_have_marked_children(folderview, item);
1501 mark = (item->marked_msgs != 0);
1503 stype = item->stype;
1504 if (stype == F_NORMAL) {
1505 if (folder_has_parent_of_type(item, F_TRASH))
1507 else if (folder_has_parent_of_type(item, F_DRAFT))
1509 else if (folder_has_parent_of_type(item, F_OUTBOX))
1511 else if (folder_has_parent_of_type(item, F_QUEUE))
1516 if (item->hide_read_msgs) {
1517 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1518 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1519 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1520 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1522 xpm = mark?m_inboxxpm:inboxxpm;
1523 mask = mark?m_inboxxpmmask:inboxxpmmask;
1524 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1525 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1529 if (item->hide_read_msgs) {
1530 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1531 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1532 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1533 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1535 xpm = mark?m_outboxxpm:outboxxpm;
1536 mask = mark?m_outboxxpmmask:outboxxpmmask;
1537 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1538 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1542 if (item->hide_read_msgs) {
1543 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1544 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1545 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1546 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1548 xpm = mark?m_queuexpm:queuexpm;
1549 mask = mark?m_queuexpmmask:queuexpmmask;
1550 openxpm = mark?m_queueopenxpm:queueopenxpm;
1551 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1555 if (item->hide_read_msgs) {
1556 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1557 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1558 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1559 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1561 xpm = mark?m_trashxpm:trashxpm;
1562 mask = mark?m_trashxpmmask:trashxpmmask;
1563 openxpm = mark?m_trashopenxpm:trashopenxpm;
1564 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1568 xpm = mark?m_draftsxpm:draftsxpm;
1569 mask = mark?m_draftsxpmmask:draftsxpmmask;
1570 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1571 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1574 if (item->hide_read_msgs) {
1575 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1576 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1577 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1578 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1580 xpm = mark?m_folderxpm:folderxpm;
1581 mask = mark?m_folderxpmmask:folderxpmmask;
1582 openxpm = mark?m_folderopenxpm:folderopenxpm;
1583 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1587 if (item->no_select) {
1588 xpm = openxpm = noselectxpm;
1589 mask = openmask = noselectxpmmask;
1592 name = folder_item_get_name(item);
1594 if (!GTK_CTREE_ROW(node)->expanded) {
1595 add_unread_mark = folderview_have_unread_children(
1597 add_sub_match_mark = folderview_have_matching_children(
1600 add_unread_mark = FALSE;
1601 add_sub_match_mark = FALSE;
1604 if (item->search_match) {
1606 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1607 &searchicon, &searchmask);
1609 xpm = openxpm = searchicon;
1610 mask = openmask = searchmask;
1614 if (prefs_common.display_folder_unread) {
1615 if (folder_has_parent_of_type(item, F_QUEUE)) {
1616 /* only total_msgs matters here */
1617 if (item->total_msgs > 0) {
1618 /* show total number (should be equal to the unread number)
1620 str = g_strdup_printf("%s (%d%s%s)",
1621 name, item->total_msgs,
1622 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1623 (item->unreadmarked_msgs > 0) ? "!" : "");
1626 if (prefs_common.display_folder_unread == 1) {
1627 if (item->unread_msgs > 0) {
1628 /* show unread number and signs */
1629 str = g_strdup_printf("%s (%d%s%s)",
1630 name, item->unread_msgs,
1631 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1632 (item->unreadmarked_msgs > 0) ? "!" : "");
1635 if (item->total_msgs > 0) {
1636 /* show unread number, total number and signs if any */
1637 str = g_strdup_printf("%s (%d/%d%s%s)",
1638 name, item->unread_msgs, item->total_msgs,
1639 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1640 (item->unreadmarked_msgs > 0) ? "!" : "");
1644 if ((str == NULL) &&
1645 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1646 /* no unread/total numbers, but at least one sign */
1647 str = g_strdup_printf("%s (%s%s)",
1649 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1650 (item->unreadmarked_msgs > 0) ? "!" : "");
1654 /* last fallback, folder name only or with ! sign */
1655 str = g_strdup_printf("%s%s",
1656 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1658 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1659 xpm, mask, openxpm, openmask,
1660 FALSE, GTK_CTREE_ROW(node)->expanded);
1664 if (!folder_item_parent(item)) {
1665 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1666 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1667 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1669 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1670 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1671 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1674 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1675 folder_has_parent_of_type(item, F_DRAFT) ||
1676 folder_has_parent_of_type(item, F_TRASH)) {
1677 use_bold = use_color = FALSE;
1678 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1679 /* highlight queue folder if there are any messages */
1680 use_bold = use_color = (item->total_msgs > 0);
1682 /* if unread messages exist, print with bold font */
1683 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1685 /* if new messages exist, print with colored letter */
1687 (item->new_msgs > 0) ||
1689 folderview_have_new_children(folderview, item));
1692 gtk_ctree_node_set_foreground(ctree, node, NULL);
1697 if (item->prefs->color > 0 && !use_color) {
1698 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1699 color_style = gtk_style_copy(bold_style);
1700 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1701 style = color_style;
1702 } else if (use_color) {
1703 style = bold_color_style;
1706 if (item->op_count > 0) {
1707 style = bold_tgtfold_style;
1709 } else if (use_color) {
1710 style = normal_color_style;
1711 gtk_ctree_node_set_foreground(ctree, node,
1712 &folderview->color_new);
1713 } else if (item->op_count > 0) {
1714 style = bold_tgtfold_style;
1715 } else if (item->prefs->color > 0) {
1717 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1718 color_style = gtk_style_copy(normal_style);
1719 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1720 style = color_style;
1722 style = normal_style;
1725 gtk_ctree_node_set_row_style(ctree, node, style);
1727 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1728 folderview_update_node(folderview, node);
1731 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1734 FolderView *folderview;
1738 g_return_if_fail(item != NULL);
1740 for (list = folderview_list; list != NULL; list = list->next) {
1741 folderview = (FolderView *)list->data;
1742 ctree = GTK_CTREE(folderview->ctree);
1744 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1746 item->search_match = matches;
1747 folderview_update_node(folderview, node);
1752 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1754 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1755 FolderView *folderview = (FolderView *)data;
1758 g_return_val_if_fail(update_info != NULL, TRUE);
1759 g_return_val_if_fail(update_info->item != NULL, TRUE);
1760 g_return_val_if_fail(folderview != NULL, FALSE);
1762 ctree = GTK_CTREE(folderview->ctree);
1764 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1767 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1768 folderview_update_node(folderview, node);
1770 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1771 update_info->item == folderview->summaryview->folder_item &&
1772 update_info->item != NULL)
1773 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1774 summary_show(folderview->summaryview, update_info->item);
1780 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1781 GNode *gnode, GtkCTreeNode *cnode,
1784 FolderView *folderview = (FolderView *)data;
1785 FolderItem *item = FOLDER_ITEM(gnode->data);
1787 g_return_val_if_fail(item != NULL, FALSE);
1789 gtk_ctree_node_set_row_data(ctree, cnode, item);
1790 folderview_update_node(folderview, cnode);
1795 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1798 FolderView *folderview = (FolderView *)data;
1801 if (GTK_CTREE_ROW(node)->children) {
1802 item = gtk_ctree_node_get_row_data(ctree, node);
1803 g_return_if_fail(item != NULL);
1805 if (!item->collapsed)
1806 gtk_ctree_expand(ctree, node);
1808 folderview_update_node(folderview, node);
1812 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1813 GtkCTreeNode *root, GtkCTreeNode **prev)
1816 GtkCTreeNode *node, *parent, *sibling;
1818 node = gtk_ctree_find_by_row_data(ctree, root, item);
1820 g_warning("%s not found.\n", item->path);
1822 parent = GTK_CTREE_ROW(node)->parent;
1823 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1824 sibling = GTK_CTREE_ROW(*prev)->sibling;
1826 sibling = GTK_CTREE_ROW(parent)->children;
1830 tmp = gtk_ctree_node_get_row_data
1832 if (tmp->stype != F_NORMAL)
1833 sibling = GTK_CTREE_ROW(sibling)->sibling;
1837 if (node != sibling)
1838 gtk_ctree_move(ctree, node, parent, sibling);
1845 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1848 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1849 GtkCTreeNode *prev = NULL;
1851 gtk_clist_freeze(GTK_CLIST(ctree));
1852 gtk_sctree_sort_recursive(ctree, root);
1853 if (root && GTK_CTREE_ROW(root)->parent) {
1854 gtk_clist_thaw(GTK_CLIST(ctree));
1857 set_special_folder(ctree, folder->inbox, root, &prev);
1858 set_special_folder(ctree, folder->outbox, root, &prev);
1859 set_special_folder(ctree, folder->draft, root, &prev);
1860 set_special_folder(ctree, folder->queue, root, &prev);
1861 set_special_folder(ctree, folder->trash, root, &prev);
1862 gtk_clist_thaw(GTK_CLIST(ctree));
1865 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1867 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1870 g_return_if_fail(folder != NULL);
1872 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1873 folderview_gnode_func, folderview);
1874 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1876 folderview_sort_folders(folderview, root, folder);
1879 /* callback functions */
1880 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1881 GdkEventButton *event)
1885 FolderViewPopup *fpopup;
1886 GtkItemFactory *fpopup_factory;
1888 FolderItem *special_trash = NULL, *special_queue = NULL;
1891 item = folderview_get_selected_item(folderview);
1893 g_return_if_fail(item != NULL);
1894 g_return_if_fail(item->folder != NULL);
1895 folder = item->folder;
1897 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1899 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1901 fpopup = g_hash_table_lookup(folderview_popups, "common");
1902 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1905 if (fpopup->set_sensitivity != NULL)
1906 fpopup->set_sensitivity(fpopup_factory, item);
1908 if (NULL != (ac = account_find_from_item(item))) {
1909 special_trash = account_get_special_folder(ac, F_TRASH);
1910 special_queue = account_get_special_folder(ac, F_QUEUE);
1913 if ((item == folder->trash || item == special_trash
1914 || folder_has_parent_of_type(item, F_TRASH)) &&
1915 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1916 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1917 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1918 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1919 && !folder_has_parent_of_type(item, F_TRASH)) {
1920 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1921 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1924 if ((item == folder->queue || item == special_queue
1925 || folder_has_parent_of_type(item, F_QUEUE)) &&
1926 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1927 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1928 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1929 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1930 && !folder_has_parent_of_type(item, F_QUEUE)) {
1931 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1932 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1935 #define SET_SENS(name, sens) \
1936 menu_set_sensitive(fpopup_factory, name, sens)
1938 if ( FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_TYPE(item->folder) == F_IMAP )
1939 SET_SENS("/Download messages", !item->no_select);
1941 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1942 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1943 folderview->selected == folderview->opened);
1944 SET_SENS("/Properties...", !item->no_select);
1946 SET_SENS("/Run processing rules", item->prefs->processing &&
1947 item->total_msgs >= 1 && !item->processing_pending);
1948 SET_SENS("/Processing...", item->node->parent != NULL &&
1949 !item->no_select && !item->processing_pending);
1951 if (item == folder->trash || item == special_trash
1952 || folder_has_parent_of_type(item, F_TRASH)) {
1953 GSList *msglist = folder_item_get_msg_list(item);
1954 SET_SENS("/Empty trash...", msglist != NULL);
1955 procmsg_msg_list_free(msglist);
1957 if (item == folder->queue || item == special_queue
1958 || folder_has_parent_of_type(item, F_QUEUE)) {
1959 GSList *msglist = folder_item_get_msg_list(item);
1960 SET_SENS("/Send queue...", msglist != NULL);
1961 procmsg_msg_list_free(msglist);
1965 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1966 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1967 event->button, event->time);
1970 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1971 FolderView *folderview)
1973 GtkCList *clist = GTK_CLIST(ctree);
1974 gint prev_row = -1, row = -1, column = -1;
1976 if (!event) return FALSE;
1978 if (event->button == 1 || event->button == 2) {
1979 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1980 folderview->open_folder = TRUE;
1982 if (event->type == GDK_2BUTTON_PRESS) {
1983 if (clist->selection) {
1986 node = GTK_CTREE_NODE(clist->selection->data);
1988 gtk_ctree_toggle_expansion(
1991 folderview->open_folder = FALSE;
1998 if (event->button == 2 || event->button == 3) {
2000 if (clist->selection) {
2003 node = GTK_CTREE_NODE(clist->selection->data);
2005 prev_row = gtkut_ctree_get_nth_from_node
2006 (GTK_CTREE(ctree), node);
2009 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
2012 if (prev_row != row) {
2013 gtk_clist_unselect_all(clist);
2014 if (event->button == 2)
2015 folderview_select_node
2017 gtk_ctree_node_nth(GTK_CTREE(ctree),
2020 gtk_clist_select_row(clist, row, column);
2024 if (event->button != 3) return FALSE;
2026 folderview_set_sens_and_popup_menu(folderview, row, event);
2030 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2031 FolderView *folderview)
2033 int row = -1, column = -1;
2035 if (!event) return FALSE;
2037 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2040 if (event->button == 1 && folderview->open_folder == FALSE &&
2041 folderview->opened != NULL) {
2042 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2043 folderview->opened);
2044 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2050 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2051 FolderView *folderview)
2053 if (!event) return FALSE;
2055 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2058 switch (event->keyval) {
2064 if (folderview->selected) {
2065 folderview_select_node(folderview,
2066 folderview->selected);
2071 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2072 gtk_ctree_toggle_expansion(
2073 GTK_CTREE(folderview->ctree),
2074 folderview->selected);
2079 if (folderview->selected) {
2080 if (folderview->opened == folderview->selected &&
2081 (!folderview->summaryview->folder_item ||
2082 folderview->summaryview->folder_item->total_msgs == 0))
2083 folderview_select_next_unread(folderview, TRUE);
2085 folderview_select_node(folderview,
2086 folderview->selected);
2096 typedef struct _PostponedSelectData
2101 FolderView *folderview;
2102 } PostponedSelectData;
2104 static gboolean postpone_select(void *data)
2106 PostponedSelectData *psdata = (PostponedSelectData *)data;
2107 debug_print("trying again\n");
2108 psdata->folderview->open_folder = TRUE;
2109 main_window_cursor_normal(psdata->folderview->mainwin);
2110 STATUSBAR_POP(psdata->folderview->mainwin);
2111 folderview_selected(psdata->ctree, psdata->row,
2112 psdata->column, psdata->folderview);
2117 void folderview_close_opened(FolderView *folderview)
2119 if (folderview->opened) {
2120 FolderItem *olditem;
2122 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2123 folderview->opened);
2125 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2126 olditem->path ? olditem->path:olditem->name);
2127 /* will be null if we just moved the previously opened folder */
2128 STATUSBAR_PUSH(folderview->mainwin, buf);
2129 main_window_cursor_wait(folderview->mainwin);
2131 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2132 summary_show(folderview->summaryview, NULL);
2133 folder_item_close(olditem);
2134 main_window_cursor_normal(folderview->mainwin);
2135 STATUSBAR_POP(folderview->mainwin);
2139 if (folderview->opened &&
2140 !GTK_CTREE_ROW(folderview->opened)->children)
2141 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2143 folderview->opened = NULL;
2145 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2146 gint column, FolderView *folderview)
2148 static gboolean can_select = TRUE; /* exclusive lock */
2153 GtkCTreeNode *old_opened = folderview->opened;
2155 folderview->selected = row;
2157 debug_print("newly selected %p, opened %p\n", folderview->selected,
2158 folderview->opened);
2159 if (folderview->opened == row) {
2160 folderview->open_folder = FALSE;
2165 item = gtk_ctree_node_get_row_data(ctree, row);
2168 folderview->open_folder = FALSE;
2172 if (!can_select || summary_is_locked(folderview->summaryview)) {
2173 if (folderview->opened) {
2174 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2175 gtk_ctree_select(ctree, folderview->opened);
2177 folderview->open_folder = FALSE;
2182 if (!folderview->open_folder) {
2189 /* Save cache for old folder */
2190 /* We don't want to lose all caches if sylpheed crashed */
2191 /* resets folderview->opened to NULL */
2192 folderview_close_opened(folderview);
2194 /* CLAWS: set compose button type: news folder items
2195 * always have a news folder as parent */
2197 toolbar_set_compose_button
2198 (folderview->mainwin->toolbar,
2199 FOLDER_TYPE(item->folder) == F_NEWS ?
2200 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2203 debug_print("Folder %s is selected\n", item->path);
2205 if (!GTK_CTREE_ROW(row)->children)
2206 gtk_ctree_expand(ctree, row);
2208 /* ungrab the mouse event */
2209 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2210 gtk_grab_remove(GTK_WIDGET(ctree));
2211 if (gdk_pointer_is_grabbed())
2212 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2216 /* TODO: wwp: avoid displaying (null) in the status bar */
2217 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2218 item->path : "(null)");
2219 debug_print("%s\n", buf);
2220 STATUSBAR_PUSH(folderview->mainwin, buf);
2223 main_window_cursor_wait(folderview->mainwin);
2225 if (folderview->scanning_folder == item->folder) {
2228 res = folder_item_open(item);
2231 if (res == -1 && item->no_select == FALSE) {
2232 main_window_cursor_normal(folderview->mainwin);
2233 STATUSBAR_POP(folderview->mainwin);
2235 alertpanel_error(_("Folder could not be opened."));
2237 folderview->open_folder = FALSE;
2241 } else if (res == -2 && item->no_select == FALSE) {
2242 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2243 data->ctree = ctree;
2245 data->column = column;
2246 data->folderview = folderview;
2247 debug_print("postponing open of %s till end of scan\n",
2248 item->path ? item->path:item->name);
2249 folderview->open_folder = FALSE;
2251 g_timeout_add(500, postpone_select, data);
2256 main_window_cursor_normal(folderview->mainwin);
2259 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2260 opened = summary_show(folderview->summaryview, item);
2262 folder_clean_cache_memory(item);
2265 gtkut_ctree_set_focus_row(ctree, old_opened);
2266 gtk_ctree_select(ctree, old_opened);
2267 folderview->opened = old_opened;
2269 folderview->opened = row;
2270 if (gtk_ctree_node_is_visible(ctree, row)
2271 != GTK_VISIBILITY_FULL)
2272 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2275 STATUSBAR_POP(folderview->mainwin);
2277 folderview->open_folder = FALSE;
2282 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2283 FolderView *folderview)
2287 item = gtk_ctree_node_get_row_data(ctree, node);
2288 g_return_if_fail(item != NULL);
2289 item->collapsed = FALSE;
2290 folderview_update_node(folderview, node);
2293 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2294 FolderView *folderview)
2298 item = gtk_ctree_node_get_row_data(ctree, node);
2299 g_return_if_fail(item != NULL);
2300 item->collapsed = TRUE;
2301 folderview_update_node(folderview, node);
2304 static void folderview_popup_close(GtkMenuShell *menu_shell,
2305 FolderView *folderview)
2307 if (!folderview->opened) return;
2309 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2312 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2313 FolderView *folderview)
2315 FolderColumnType type = folderview->col_state[column].type;
2317 prefs_common.folder_col_size[type] = width;
2320 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2324 folderview_create_folder_node(folderview, item);
2326 if (!item || !item->folder || !item->folder->node)
2329 srcnode = item->folder->node;
2330 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2331 srcnode = srcnode->children;
2332 while (srcnode != NULL) {
2333 if (srcnode && srcnode->data) {
2334 FolderItem *next_item = (FolderItem*) srcnode->data;
2335 folderview_create_folder_node_recursive(folderview, next_item);
2337 srcnode = srcnode->next;
2341 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2343 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2344 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2345 GtkCTreeNode *node, *parent_node;
2346 gint *col_pos = folderview->col_pos;
2347 FolderItemUpdateData hookdata;
2349 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2350 if (parent_node == NULL)
2353 gtk_clist_freeze(GTK_CLIST(ctree));
2355 text[col_pos[F_COL_FOLDER]] = item->name;
2356 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2358 folderxpm, folderxpmmask,
2359 folderopenxpm, folderopenxpmmask,
2361 gtk_ctree_expand(ctree, parent_node);
2362 gtk_ctree_node_set_row_data(ctree, node, item);
2364 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2365 folderview_sort_folders(folderview, parent_node, item->folder);
2367 hookdata.item = item;
2368 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2369 hookdata.msg = NULL;
2370 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2372 gtk_clist_thaw(GTK_CLIST(ctree));
2375 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2378 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2380 GSList *mlist = NULL;
2382 FolderItem *special_trash = NULL;
2385 if (!folderview->selected) return;
2386 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2387 g_return_if_fail(item != NULL);
2388 g_return_if_fail(item->folder != NULL);
2390 if (NULL != (ac = account_find_from_item(item)))
2391 special_trash = account_get_special_folder(ac, F_TRASH);
2393 if (item != item->folder->trash && item != special_trash
2394 && !folder_has_parent_of_type(item, F_TRASH)) return;
2396 if (prefs_common.ask_on_clean) {
2397 if (alertpanel(_("Empty trash"),
2398 _("Delete all messages in trash?"),
2399 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2403 mlist = folder_item_get_msg_list(item);
2405 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2406 MsgInfo * msginfo = (MsgInfo *) cur->data;
2407 if (MSG_IS_LOCKED(msginfo->flags))
2409 /* is it partially received? (partial_recv isn't cached) */
2410 if (msginfo->total_size != 0 &&
2411 msginfo->size != (off_t)msginfo->total_size)
2412 partial_mark_for_delete(msginfo);
2414 procmsg_msg_list_free(mlist);
2416 folder_item_remove_all_msg(item);
2419 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2422 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2424 FolderItem *special_queue = NULL;
2426 gchar *errstr = NULL;
2428 if (!folderview->selected) return;
2429 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2430 g_return_if_fail(item != NULL);
2431 g_return_if_fail(item->folder != NULL);
2433 if (NULL != (ac = account_find_from_item(item)))
2434 special_queue = account_get_special_folder(ac, F_QUEUE);
2436 if (item != item->folder->queue && item != special_queue
2437 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2439 if (procmsg_queue_is_empty(item))
2442 if (prefs_common.work_offline)
2443 if (alertpanel(_("Offline warning"),
2444 _("You're working offline. Override?"),
2445 GTK_STOCK_NO, GTK_STOCK_YES,
2446 NULL) != G_ALERTALTERNATE)
2449 /* ask for confirmation before sending queued messages only
2450 in online mode and if there is at least one message queued
2451 in any of the folder queue
2453 if (prefs_common.confirm_send_queued_messages) {
2454 if (!prefs_common.work_offline) {
2455 if (alertpanel(_("Send queued messages"),
2456 _("Send all queued messages?"),
2457 GTK_STOCK_CANCEL, _("_Send"),
2458 NULL) != G_ALERTALTERNATE)
2463 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2465 alertpanel_error_log(_("Some errors occurred while "
2466 "sending queued messages."));
2468 alertpanel_error_log(_("Some errors occurred "
2469 "while sending queued messages:\n%s"), errstr);
2475 static void folderview_search_cb(FolderView *folderview, guint action,
2478 summary_search(folderview->summaryview);
2481 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2484 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2487 if (!folderview->selected) return;
2489 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2490 g_return_if_fail(item != NULL);
2491 g_return_if_fail(item->folder != NULL);
2493 item->processing_pending = TRUE;
2494 folder_item_apply_processing(item);
2495 item->processing_pending = FALSE;
2498 static void folderview_property_cb(FolderView *folderview, guint action,
2501 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2504 if (!folderview->selected) return;
2506 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2507 g_return_if_fail(item != NULL);
2508 g_return_if_fail(item->folder != NULL);
2510 prefs_folder_item_open(item);
2513 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2515 GSList *list = NULL;
2516 GSList *done = NULL;
2517 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2519 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2520 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2521 && list->data != node) {
2522 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2523 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2526 for (list = done; list != NULL; list = g_slist_next(list)) {
2527 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2533 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2534 FolderItem *to_folder, gboolean copy)
2536 FolderItem *from_parent = NULL;
2537 FolderItem *new_folder = NULL;
2538 GtkCTreeNode *src_node = NULL;
2542 g_return_if_fail(folderview != NULL);
2543 g_return_if_fail(from_folder != NULL);
2544 g_return_if_fail(to_folder != NULL);
2546 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2547 from_parent = folder_item_parent(from_folder);
2549 if (prefs_common.warn_dnd) {
2550 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2551 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2552 from_folder->name, to_folder->name);
2553 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2554 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2555 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2558 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2560 else if (status & G_ALERTDISABLE)
2561 prefs_common.warn_dnd = FALSE;
2564 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2565 from_folder->name, to_folder->name);
2566 STATUSBAR_PUSH(folderview->mainwin, buf);
2568 summary_clear_all(folderview->summaryview);
2569 folderview->opened = NULL;
2570 folderview->selected = NULL;
2571 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2573 main_window_cursor_wait(folderview->mainwin);
2575 statusbar_verbosity_set(FALSE);
2576 folder_item_update_freeze();
2577 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2578 statusbar_verbosity_set(FALSE);
2579 main_window_cursor_normal(folderview->mainwin);
2580 STATUSBAR_POP(folderview->mainwin);
2581 folder_item_update_thaw();
2582 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2584 folderview_sort_folders(folderview,
2585 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2586 NULL, to_folder), new_folder->folder);
2587 folderview_select(folderview, new_folder);
2589 statusbar_verbosity_set(FALSE);
2590 main_window_cursor_normal(folderview->mainwin);
2591 STATUSBAR_POP(folderview->mainwin);
2592 folder_item_update_thaw();
2594 case F_MOVE_FAILED_DEST_IS_PARENT:
2595 alertpanel_error(_("Source and destination are the same."));
2597 case F_MOVE_FAILED_DEST_IS_CHILD:
2598 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2599 _("Can't move a folder to one of its children."));
2601 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2602 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2605 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2610 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2613 static gint folderview_clist_compare(GtkCList *clist,
2614 gconstpointer ptr1, gconstpointer ptr2)
2616 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2617 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2619 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2621 return item1->order - item2->order;
2624 // if only one folder has an order it comes first
2625 if (item1->order > 0)
2629 if (item2->order > 0)
2635 return (item2->name != NULL);
2639 return g_utf8_collate(item1->name, item2->name);
2642 static void folderview_processing_cb(FolderView *folderview, guint action,
2645 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2649 if (!folderview->selected) return;
2651 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2652 g_return_if_fail(item != NULL);
2653 g_return_if_fail(item->folder != NULL);
2655 id = folder_item_get_identifier(item);
2656 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2659 prefs_filtering_open(&item->prefs->processing, title,
2660 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2664 void folderview_set_target_folder_color(gint color_op)
2668 FolderView *folderview;
2670 for (list = folderview_list; list != NULL; list = list->next) {
2671 folderview = (FolderView *)list->data;
2672 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2674 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2675 folderview->color_op;
2681 static gchar *last_font = NULL;
2682 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2690 void folderview_reflect_prefs(void)
2692 gboolean update_font = TRUE;
2693 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2694 FolderItem *item = folderview_get_selected_item(folderview);
2695 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2696 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2697 gint height = pos->value;
2699 if (last_font && !strcmp(last_font, NORMAL_FONT))
2700 update_font = FALSE;
2703 last_font = g_strdup(NORMAL_FONT);
2706 normal_style = normal_color_style = bold_style =
2707 bold_color_style = bold_tgtfold_style = NULL;
2709 folderview_init(folderview);
2711 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2712 folderview_column_set_titles(folderview);
2713 folderview_set_all();
2715 g_signal_handlers_block_by_func
2716 (G_OBJECT(folderview->ctree),
2717 G_CALLBACK(folderview_selected), folderview);
2720 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2721 GTK_CTREE(folderview->ctree), NULL, item);
2723 folderview_select(folderview, item);
2724 folderview->open_folder = FALSE;
2725 folderview->selected = node;
2728 g_signal_handlers_unblock_by_func
2729 (G_OBJECT(folderview->ctree),
2730 G_CALLBACK(folderview_selected), folderview);
2732 pos = gtk_scrolled_window_get_vadjustment(
2733 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2734 gtk_adjustment_set_value(pos, height);
2735 gtk_adjustment_changed(pos);
2736 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2739 static void drag_state_stop(FolderView *folderview)
2741 if (folderview->drag_timer)
2742 g_source_remove(folderview->drag_timer);
2743 folderview->drag_timer = 0;
2744 folderview->drag_node = NULL;
2747 static gint folderview_defer_expand(FolderView *folderview)
2749 if (folderview->drag_node) {
2750 folderview_recollapse_nodes(folderview, folderview->drag_node);
2751 if (folderview->drag_item->collapsed) {
2752 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2753 folderview->nodes_to_recollapse = g_slist_append
2754 (folderview->nodes_to_recollapse, folderview->drag_node);
2757 folderview->drag_item = NULL;
2758 folderview->drag_timer = 0;
2762 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2764 /* the idea is that we call drag_state_start() whenever we want expansion to
2765 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2766 * we need to call drag_state_stop() */
2767 drag_state_stop(folderview);
2768 /* request expansion */
2769 if (0 != (folderview->drag_timer = g_timeout_add
2770 (prefs_common.hover_timeout,
2771 (GtkFunction)folderview_defer_expand,
2773 folderview->drag_node = node;
2774 folderview->drag_item = item;
2778 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2779 FolderView *folderview)
2781 GdkDragContext *context;
2783 g_return_if_fail(folderview != NULL);
2784 if (folderview->selected == NULL) return;
2785 if (folderview->nodes_to_recollapse)
2786 g_slist_free(folderview->nodes_to_recollapse);
2787 folderview->nodes_to_recollapse = NULL;
2788 context = gtk_drag_begin(widget, folderview->target_list,
2789 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2790 gtk_drag_set_icon_default(context);
2793 static void folderview_drag_data_get(GtkWidget *widget,
2794 GdkDragContext *drag_context,
2795 GtkSelectionData *selection_data,
2798 FolderView *folderview)
2802 gchar *source = NULL;
2803 if (info == TARGET_DUMMY) {
2804 for (cur = GTK_CLIST(folderview->ctree)->selection;
2805 cur != NULL; cur = cur->next) {
2806 item = gtk_ctree_node_get_row_data
2807 (GTK_CTREE(folderview->ctree),
2808 GTK_CTREE_NODE(cur->data));
2810 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2811 gtk_selection_data_set(selection_data,
2812 selection_data->target, 8,
2813 source, strlen(source));
2819 g_warning("unknown info %d\n", info);
2823 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2825 FolderUpdateData *hookdata;
2826 FolderView *folderview;
2830 folderview = (FolderView *) userdata;
2831 g_return_val_if_fail(hookdata != NULL, FALSE);
2832 g_return_val_if_fail(folderview != NULL, FALSE);
2834 ctree = folderview->ctree;
2835 g_return_val_if_fail(ctree != NULL, FALSE);
2837 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2838 folderview_create_folder_node(folderview, hookdata->item);
2839 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2840 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2841 NULL, folder_item_parent(hookdata->item));
2842 folderview_sort_folders(folderview, node, hookdata->folder);
2843 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2846 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2848 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2849 if (folderview->selected == node)
2850 folderview->selected = NULL;
2851 if (folderview->opened == node)
2852 folderview->opened = NULL;
2854 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2855 folderview_set(folderview);
2860 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2861 GdkDragContext *context,
2865 FolderView *folderview)
2868 FolderItem *item = NULL, *src_item = NULL;
2869 GtkCTreeNode *node = NULL;
2870 gboolean acceptable = FALSE;
2871 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2872 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2873 int height = (int)pos->page_size;
2874 int total_height = (int)pos->upper;
2875 int vpos = (int) pos->value;
2876 int offset = prefs_common.show_col_headers ? 24:0;
2878 if (gtk_clist_get_selection_info
2879 (GTK_CLIST(widget), x - offset, y - offset, &row, &column)) {
2880 GtkWidget *srcwidget;
2882 if (y > height - 24 && height + vpos < total_height) {
2883 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2884 gtk_adjustment_changed(pos);
2886 if (y < 48 && y > 0) {
2887 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2888 gtk_adjustment_changed(pos);
2891 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2892 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2893 src_item = folderview->summaryview->folder_item;
2895 srcwidget = gtk_drag_get_source_widget(context);
2896 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2897 /* comes from summaryview */
2898 /* we are copying messages, so only accept folder items that are not
2899 the source item, are no root items and can copy messages */
2900 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2901 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2902 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2904 } else if (srcwidget == folderview->ctree) {
2905 /* comes from folderview */
2906 /* we are moving folder items, only accept folders that are not
2907 the source items and can copy messages and create folder items */
2908 if (item && item->folder && src_item && src_item != item &&
2909 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2910 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2911 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2912 || item->folder == src_item->folder))
2915 /* comes from another app */
2916 /* we are adding messages, so only accept folder items that are
2917 no root items and can copy messages */
2918 if (item && item->folder && folder_item_parent(item) != NULL
2919 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2920 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2925 if (acceptable || (src_item && src_item == item))
2926 drag_state_start(folderview, node, item);
2929 g_signal_handlers_block_by_func
2931 G_CALLBACK(folderview_selected), folderview);
2932 gtk_ctree_select(GTK_CTREE(widget), node);
2933 g_signal_handlers_unblock_by_func
2935 G_CALLBACK(folderview_selected), folderview);
2936 gdk_drag_status(context,
2937 (context->actions == GDK_ACTION_COPY ?
2938 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2940 if (folderview->opened)
2941 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2942 gdk_drag_status(context, 0, time);
2948 static void folderview_drag_leave_cb(GtkWidget *widget,
2949 GdkDragContext *context,
2951 FolderView *folderview)
2953 drag_state_stop(folderview);
2954 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2957 static void free_info (gpointer stuff, gpointer data)
2962 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2963 guint time, FolderItem *item)
2966 GSList *msglist = NULL;
2967 list = uri_list_extract_filenames(data);
2968 if (!(item && item->folder && folder_item_parent(item) != NULL
2969 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2971 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2972 debug_print("item doesn't fit\n");
2976 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2977 debug_print("list is empty\n");
2980 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2981 MsgFileInfo *info = NULL;
2983 if (file_is_email((gchar *)tmp->data)) {
2984 info = g_new0(MsgFileInfo, 1);
2985 info->msginfo = NULL;
2986 info->file = (gchar *)tmp->data;
2987 msglist = g_slist_prepend(msglist, info);
2988 debug_print("file is a mail\n");
2990 debug_print("file isn't a mail\n");
2994 msglist = g_slist_reverse(msglist);
2995 folder_item_add_msgs(item, msglist, FALSE);
2996 g_slist_foreach(msglist, free_info, NULL);
2997 g_slist_free(msglist);
2998 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3000 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3002 list_free_strings(list);
3006 static void folderview_drag_received_cb(GtkWidget *widget,
3007 GdkDragContext *drag_context,
3010 GtkSelectionData *data,
3013 FolderView *folderview)
3016 FolderItem *item = NULL, *src_item;
3018 int offset = prefs_common.show_col_headers ? 24:0;
3020 if (info == TARGET_DUMMY) {
3021 drag_state_stop(folderview);
3022 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3023 /* comes from summaryview */
3024 if (gtk_clist_get_selection_info
3025 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3028 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3029 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3030 src_item = folderview->summaryview->folder_item;
3032 if (item->no_select) {
3033 alertpanel_error(_("The destination folder can only be used to "
3034 "store subfolders."));
3037 /* re-check (due to acceptable possibly set for folder moves */
3038 if (!(item && item->folder && item->path && !item->no_select &&
3039 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3042 if (item && src_item) {
3043 switch (drag_context->action) {
3044 case GDK_ACTION_COPY:
3045 summary_copy_selected_to(folderview->summaryview, item);
3046 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3048 case GDK_ACTION_MOVE:
3049 case GDK_ACTION_DEFAULT:
3051 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3052 summary_copy_selected_to(folderview->summaryview, item);
3054 summary_move_selected_to(folderview->summaryview, item);
3055 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3058 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3060 /* comes from folderview */
3062 gboolean folder_is_normal = TRUE;
3063 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3065 source = data->data + 17;
3066 if (gtk_clist_get_selection_info
3067 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0
3069 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3072 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3073 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3074 src_item = folder_find_item_from_identifier(source);
3078 src_item->stype == F_NORMAL &&
3079 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3080 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3081 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3082 !folder_has_parent_of_type(src_item, F_TRASH);
3083 if (!item || !src_item || !folder_is_normal) {
3084 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3088 folderview_move_folder(folderview, src_item, item, copy);
3089 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3091 folderview->nodes_to_recollapse = NULL;
3092 } else if (info == TARGET_MAIL_URI_LIST) {
3093 if (gtk_clist_get_selection_info
3094 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3097 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3099 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3100 debug_print("no node\n");
3103 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3105 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3106 debug_print("no item\n");
3109 folderview_finish_dnd(data->data, drag_context, time, item);
3113 static void folderview_drag_end_cb(GtkWidget *widget,
3114 GdkDragContext *drag_context,
3115 FolderView *folderview)
3117 drag_state_stop(folderview);
3118 g_slist_free(folderview->nodes_to_recollapse);
3119 folderview->nodes_to_recollapse = NULL;
3122 void folderview_register_popup(FolderViewPopup *fpopup)
3126 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3127 FolderView *folderview = folderviews->data;
3128 GtkItemFactory *factory;
3130 factory = create_ifactory(folderview, fpopup);
3131 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3133 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3136 void folderview_unregister_popup(FolderViewPopup *fpopup)
3140 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3141 FolderView *folderview = folderviews->data;
3143 g_hash_table_remove(folderview->popups, fpopup->klass);
3145 g_hash_table_remove(folderview_popups, fpopup->klass);