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)
606 GtkWidget *ctree = folderview->ctree;
607 FolderItem *item = folderview_get_selected_item(folderview);
608 FolderItem *sel_item = NULL, *op_item = NULL;
609 GtkWidget *scrolledwin = folderview->scrolledwin;
611 if (folderview->selected)
612 sel_item = gtk_ctree_node_get_row_data(GTK_CTREE(ctree), folderview->selected);
613 if (folderview->opened)
614 op_item = gtk_ctree_node_get_row_data(GTK_CTREE(ctree), folderview->opened);
616 debug_print("recreating tree...\n");
617 gtk_widget_destroy(folderview->ctree);
620 folderview->ctree = ctree = folderview_ctree_create(folderview);
621 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
622 GTK_CLIST(ctree)->hadjustment);
623 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
624 GTK_CLIST(ctree)->vadjustment);
625 gtk_widget_show(ctree);
628 folderview->selected = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, sel_item);
630 folderview->opened = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, op_item);
632 folderview_set(folderview);
633 folderview_column_set_titles(folderview);
635 folderview_select(folderview,item);
638 FolderView *folderview_create(void)
640 FolderView *folderview;
641 GtkWidget *scrolledwin;
644 debug_print("Creating folder view...\n");
645 folderview = g_new0(FolderView, 1);
647 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
648 gtk_scrolled_window_set_policy
649 (GTK_SCROLLED_WINDOW(scrolledwin),
650 GTK_POLICY_AUTOMATIC,
651 prefs_common.folderview_vscrollbar_policy);
652 gtk_widget_set_size_request(scrolledwin,
653 prefs_common.folderview_width,
654 prefs_common.folderview_height);
656 folderview->scrolledwin = scrolledwin;
657 ctree = folderview_ctree_create(folderview);
659 /* create popup factories */
660 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
661 g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
663 folderview->ctree = ctree;
665 folderview->folder_update_callback_id =
666 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
667 folderview->folder_item_update_callback_id =
668 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
670 gtk_widget_show_all(scrolledwin);
672 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
673 folderview_list = g_list_append(folderview_list, folderview);
674 folderview->deferred_refresh_id = -1;
679 void folderview_init(FolderView *folderview)
681 GtkWidget *ctree = folderview->ctree;
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
686 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
687 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
688 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
689 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
690 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
691 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
692 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
693 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
694 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
695 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
696 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
697 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
698 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
699 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
700 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
701 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
702 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
703 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
704 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
705 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
706 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
708 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
709 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
710 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
711 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
712 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
713 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
714 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
715 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
716 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
717 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
718 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
719 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
720 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
721 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
722 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
723 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
724 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
725 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
726 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
727 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
728 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
729 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
732 PangoFontDescription *font_desc;
733 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
734 font_desc = pango_font_description_from_string(NORMAL_FONT);
736 if (normal_style->font_desc)
737 pango_font_description_free
738 (normal_style->font_desc);
739 normal_style->font_desc = font_desc;
741 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
742 normal_color_style = gtk_style_copy(normal_style);
743 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
745 gtk_widget_set_style(ctree, normal_style);
749 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
750 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
751 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
752 pango_font_description_set_weight
753 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
755 PangoFontDescription *font_desc;
756 font_desc = pango_font_description_from_string(BOLD_FONT);
758 if (bold_style->font_desc)
759 pango_font_description_free
760 (bold_style->font_desc);
761 bold_style->font_desc = font_desc;
764 bold_color_style = gtk_style_copy(bold_style);
765 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
767 bold_tgtfold_style = gtk_style_copy(bold_style);
768 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
772 static gboolean folderview_defer_set(gpointer data)
774 FolderView *folderview = (FolderView *)data;
775 MainWindow *mainwin = folderview->mainwin;
779 if (mainwin->lock_count)
782 debug_print("doing deferred folderview_set now\n");
783 folderview_set(folderview);
785 folderview->deferred_refresh_id = -1;
789 void folderview_set(FolderView *folderview)
791 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
792 MainWindow *mainwin = folderview->mainwin;
793 FolderItem *sel_item = NULL, *op_item = NULL;
798 if (mainwin->lock_count) {
799 if (folderview->deferred_refresh_id == -1)
800 folderview->deferred_refresh_id =
801 g_timeout_add(500, folderview_defer_set, folderview);
802 debug_print("deferred folderview_set\n");
807 debug_print("Setting folder info...\n");
808 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
810 main_window_cursor_wait(mainwin);
812 if (folderview->selected)
813 sel_item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
814 if (folderview->opened)
815 op_item = gtk_ctree_node_get_row_data(ctree, folderview->opened);
817 folderview->selected = NULL;
818 folderview->opened = NULL;
820 gtk_clist_freeze(GTK_CLIST(ctree));
821 gtk_clist_clear(GTK_CLIST(ctree));
823 folderview_set_folders(folderview);
826 folderview->selected = gtk_ctree_find_by_row_data(ctree, NULL, sel_item);
828 folderview->opened = gtk_ctree_find_by_row_data(ctree, NULL, op_item);
830 gtk_clist_thaw(GTK_CLIST(ctree));
831 main_window_cursor_normal(mainwin);
832 STATUSBAR_POP(mainwin);
836 void folderview_set_all(void)
840 for (list = folderview_list; list != NULL; list = list->next)
841 folderview_set((FolderView *)list->data);
844 void folderview_select(FolderView *folderview, FolderItem *item)
846 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
848 GtkCTreeNode *old_selected = folderview->selected;
852 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
853 if (node) folderview_select_node(folderview, node);
855 if (old_selected != node)
856 folder_update_op_count();
859 static void mark_all_read_cb(FolderView *folderview, guint action,
865 item = folderview_get_selected_item(folderview);
869 if (folderview->summaryview->folder_item != item
870 && prefs_common.ask_mark_all_read) {
871 val = alertpanel_full(_("Mark all as read"),
872 _("Do you really want to mark all mails in this "
873 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
874 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
876 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
878 else if (val & G_ALERTDISABLE)
879 prefs_common.ask_mark_all_read = FALSE;
883 folder_item_update_freeze();
884 if (folderview->summaryview->folder_item != item)
885 summary_lock(folderview->summaryview);
887 summary_freeze(folderview->summaryview);
889 folderutils_mark_all_read(item);
891 if (folderview->summaryview->folder_item != item)
892 summary_unlock(folderview->summaryview);
894 summary_thaw(folderview->summaryview);
895 folder_item_update_thaw();
898 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
900 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
902 g_return_if_fail(node != NULL);
904 if (folderview->open_folder) {
908 folderview->open_folder = TRUE;
909 gtkut_ctree_set_focus_row(ctree, node);
910 gtk_ctree_select(ctree, node);
911 if (folderview->summaryview->folder_item &&
912 folderview->summaryview->folder_item->total_msgs > 0)
913 summary_grab_focus(folderview->summaryview);
915 gtk_widget_grab_focus(folderview->ctree);
917 gtkut_ctree_expand_parent_all(ctree, node);
920 void folderview_unselect(FolderView *folderview)
922 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
924 (GTK_CTREE(folderview->ctree), folderview->opened);
926 folderview->selected = folderview->opened = NULL;
929 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
935 node = gtkut_ctree_node_next(ctree, node);
937 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
939 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
940 item = gtk_ctree_node_get_row_data(ctree, node);
941 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
948 void folderview_select_next_marked(FolderView *folderview)
950 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
951 GtkCTreeNode *node = NULL;
952 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
953 gboolean last_open = prefs_common.always_show_msg;
955 prefs_common.summary_select_prio[0] = ACTION_MARKED;
956 prefs_common.always_show_msg = OPENMSG_ALWAYS;
958 if ((node = folderview_find_next_marked(ctree, folderview->opened))
960 folderview_select_node(folderview, node);
964 if (!folderview->opened ||
965 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
968 /* search again from the first node */
969 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
970 folderview_select_node(folderview, node);
973 prefs_common.summary_select_prio[0] = last_summary_select_prio;
974 prefs_common.always_show_msg = last_open;
977 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
983 node = gtkut_ctree_node_next(ctree, node);
985 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
987 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
988 item = gtk_ctree_node_get_row_data(ctree, node);
989 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
996 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
998 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
999 GtkCTreeNode *node = NULL;
1000 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1001 gboolean last_open = prefs_common.always_show_msg;
1003 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
1004 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
1006 if ((node = folderview_find_next_unread(ctree, folderview->opened))
1008 folderview_select_node(folderview, node);
1012 if (!folderview->opened ||
1013 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1016 /* search again from the first node */
1017 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
1018 folderview_select_node(folderview, node);
1021 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1022 prefs_common.always_show_msg = last_open;
1025 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
1031 node = gtkut_ctree_node_next(ctree, node);
1033 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1035 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1036 item = gtk_ctree_node_get_row_data(ctree, node);
1037 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
1044 void folderview_select_next_new(FolderView *folderview)
1046 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1047 GtkCTreeNode *node = NULL;
1048 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1049 gboolean last_open = prefs_common.always_show_msg;
1051 prefs_common.summary_select_prio[0] = ACTION_NEW;
1052 prefs_common.always_show_msg = OPENMSG_ALWAYS;
1054 if ((node = folderview_find_next_new(ctree, folderview->opened))
1056 folderview_select_node(folderview, node);
1060 if (!folderview->opened ||
1061 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1064 /* search again from the first node */
1065 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1066 folderview_select_node(folderview, node);
1069 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1070 prefs_common.always_show_msg = last_open;
1073 FolderItem *folderview_get_selected_item(FolderView *folderview)
1075 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1077 if (!folderview->selected) return NULL;
1078 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1081 static void folderview_set_folders(FolderView *folderview)
1084 list = folder_get_list();
1086 for (; list != NULL; list = list->next) {
1087 folderview_append_folder(folderview, FOLDER(list->data));
1091 static gchar *get_scan_str(FolderItem *item)
1094 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1095 item->folder->name, G_DIR_SEPARATOR,
1098 return g_strdup_printf(_("Scanning folder %s ..."),
1099 item->folder->name);
1101 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1105 for (list = folderview_list; list != NULL; list = list->next) {
1106 FolderView *folderview = (FolderView *)list->data;
1107 MainWindow *mainwin = folderview->mainwin;
1108 gchar *str = get_scan_str(item);
1110 STATUSBAR_PUSH(mainwin, str);
1111 STATUSBAR_POP(mainwin);
1116 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1119 MainWindow *mainwin = mainwindow_get_mainwindow();
1120 FolderView *folderview = NULL;
1121 GtkAdjustment *pos = NULL;
1124 g_return_if_fail(folder != NULL);
1126 if (!folder->klass->scan_tree) return;
1129 alertpanel_full(_("Rebuild folder tree"),
1130 _("Rebuilding the folder tree will remove "
1131 "local caches. Do you want to continue?"),
1132 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1133 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1134 != G_ALERTALTERNATE) {
1140 window = label_window_create(_("Rebuilding folder tree..."));
1142 window = label_window_create(_("Scanning folder tree..."));
1145 folderview = mainwin->folderview;
1148 pos = gtk_scrolled_window_get_vadjustment(
1149 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1150 height = pos->value;
1153 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1154 folder_scan_tree(folder, rebuild);
1155 folder_set_ui_func(folder, NULL, NULL);
1157 folderview_set_all();
1160 pos = gtk_scrolled_window_get_vadjustment(
1161 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1162 gtk_adjustment_set_value(pos, height);
1163 gtk_adjustment_changed(pos);
1165 label_window_destroy(window);
1169 void folderview_fast_rescan_tree(Folder *folder)
1172 MainWindow *mainwin = mainwindow_get_mainwindow();
1173 FolderView *folderview = NULL;
1174 GtkAdjustment *pos = NULL;
1177 g_return_if_fail(folder != NULL);
1179 if (!folder->klass->scan_tree) return;
1183 window = label_window_create(_("Scanning folder tree..."));
1186 folderview = mainwin->folderview;
1189 pos = gtk_scrolled_window_get_vadjustment(
1190 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1191 height = pos->value;
1194 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1195 folder_fast_scan_tree(folder);
1196 folder_set_ui_func(folder, NULL, NULL);
1198 folderview_set_all();
1201 pos = gtk_scrolled_window_get_vadjustment(
1202 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1203 gtk_adjustment_set_value(pos, height);
1204 gtk_adjustment_changed(pos);
1206 label_window_destroy(window);
1210 /** folderview_check_new()
1211 * Scan and update the folder and return the
1212 * count the number of new messages since last check.
1213 * \param folder the folder to check for new messages
1214 * \return the number of new messages since last check
1216 gint folderview_check_new(Folder *folder)
1220 FolderView *folderview;
1224 gint former_new_msgs = 0;
1225 gint former_new = 0, former_unread = 0, former_total;
1227 for (list = folderview_list; list != NULL; list = list->next) {
1228 folderview = (FolderView *)list->data;
1229 ctree = GTK_CTREE(folderview->ctree);
1230 folderview->scanning_folder = folder;
1232 main_window_lock(folderview->mainwin);
1234 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1235 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1237 item = gtk_ctree_node_get_row_data(ctree, node);
1238 if (!item || !item->path || !item->folder) continue;
1239 if (item->no_select) continue;
1240 if (folder && folder != item->folder) continue;
1241 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1242 if (!item->prefs->newmailcheck) continue;
1243 if (item->processing_pending == TRUE) {
1244 debug_print("skipping %s, processing pending\n",
1245 item->path ? item->path : item->name);
1248 if (item->scanning != ITEM_NOT_SCANNING) {
1249 debug_print("skipping %s, scanning\n",
1250 item->path ? item->path : item->name);
1254 str = get_scan_str(item);
1256 STATUSBAR_PUSH(folderview->mainwin, str);
1260 folderview_scan_tree_func(item->folder, item, NULL);
1261 former_new = item->new_msgs;
1262 former_unread = item->unread_msgs;
1263 former_total = item->total_msgs;
1265 if (item->folder->klass->scan_required &&
1266 (item->folder->klass->scan_required(item->folder, item) ||
1267 item->folder->inbox == item ||
1268 item->opened == TRUE ||
1269 item->processing_pending == TRUE)) {
1270 if (folder_item_scan(item) < 0) {
1272 summaryview_unlock(folderview->summaryview, item);
1273 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1274 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1275 item->path ? item->path:item->name);
1276 STATUSBAR_POP(folderview->mainwin);
1278 } else if (!FOLDER_IS_LOCAL(folder)) {
1279 STATUSBAR_POP(folderview->mainwin);
1284 } else if (!item->folder->klass->scan_required) {
1285 if (folder_item_scan(item) < 0) {
1286 summaryview_unlock(folderview->summaryview, item);
1287 if (folder && !FOLDER_IS_LOCAL(folder)) {
1288 STATUSBAR_POP(folderview->mainwin);
1293 if (former_new != item->new_msgs ||
1294 former_unread != item->unread_msgs ||
1295 former_total != item->total_msgs)
1296 folderview_update_node(folderview, node);
1298 new_msgs += item->new_msgs;
1299 former_new_msgs += former_new;
1300 STATUSBAR_POP(folderview->mainwin);
1302 folderview->scanning_folder = NULL;
1303 main_window_unlock(folderview->mainwin);
1307 folder_write_list();
1308 /* Number of new messages since last check is the just the difference
1309 * between former_new_msgs and new_msgs. If new_msgs is less than
1310 * former_new_msgs, that would mean another session accessed the folder
1311 * and the result is not well defined.
1313 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1317 void folderview_check_new_all(void)
1321 FolderView *folderview;
1323 folderview = (FolderView *)folderview_list->data;
1326 main_window_lock(folderview->mainwin);
1327 window = label_window_create
1328 (_("Checking for new messages in all folders..."));
1330 list = folder_get_list();
1331 for (; list != NULL; list = list->next) {
1332 Folder *folder = list->data;
1334 folderview_check_new(folder);
1337 folder_write_list();
1338 folderview_set_all();
1340 label_window_destroy(window);
1341 main_window_unlock(folderview->mainwin);
1345 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1351 if (!item || !item->folder || !item->folder->node)
1354 node = item->folder->node;
1356 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1357 node = node->children;
1360 (item->new_msgs > 0 ||
1361 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1365 while (node != NULL) {
1366 if (node && node->data) {
1367 FolderItem *next_item = (FolderItem*) node->data;
1369 if (folderview_have_new_children_sub(folderview,
1378 static gboolean folderview_have_new_children(FolderView *folderview,
1381 return folderview_have_new_children_sub(folderview, item, FALSE);
1384 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1390 if (!item || !item->folder || !item->folder->node)
1393 node = item->folder->node;
1395 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1396 node = node->children;
1399 (item->unread_msgs > 0 ||
1400 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1404 while (node != NULL) {
1405 if (node && node->data) {
1406 FolderItem *next_item = (FolderItem*) node->data;
1408 if (folderview_have_unread_children_sub(folderview,
1418 static gboolean folderview_have_unread_children(FolderView *folderview,
1421 return folderview_have_unread_children_sub(folderview, item, FALSE);
1424 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1430 if (!item || !item->folder || !item->folder->node)
1433 node = item->folder->node;
1435 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1436 node = node->children;
1438 if (in_sub && item->search_match){
1442 while (node != NULL) {
1443 if (node && node->data) {
1444 FolderItem *next_item = (FolderItem*) node->data;
1446 if (folderview_have_matching_children_sub(folderview,
1456 static gboolean folderview_have_matching_children(FolderView *folderview,
1459 return folderview_have_matching_children_sub(folderview, item, FALSE);
1462 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1468 if (!item || !item->folder || !item->folder->node)
1471 node = item->folder->node;
1473 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1474 node = node->children;
1476 if (item->marked_msgs != 0) {
1480 while (node != NULL) {
1481 if (node && node->data) {
1482 FolderItem *next_item = (FolderItem*) node->data;
1484 if (folderview_have_marked_children_sub(folderview,
1493 static gboolean folderview_have_marked_children(FolderView *folderview,
1496 return folderview_have_marked_children_sub(folderview, item, FALSE);
1499 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1501 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1502 GtkStyle *style = NULL;
1503 GtkStyle *color_style = NULL;
1505 GdkPixmap *xpm, *openxpm;
1506 GdkBitmap *mask, *openmask;
1507 static GdkPixmap *searchicon;
1508 static GdkBitmap *searchmask;
1509 gboolean mark = FALSE;
1512 gboolean add_unread_mark;
1513 gboolean add_sub_match_mark;
1514 gboolean use_bold, use_color;
1515 gint *col_pos = folderview->col_pos;
1516 SpecialFolderItemType stype;
1518 item = gtk_ctree_node_get_row_data(ctree, node);
1519 g_return_if_fail(item != NULL);
1521 if (!GTK_CTREE_ROW(node)->expanded)
1522 mark = folderview_have_marked_children(folderview, item);
1524 mark = (item->marked_msgs != 0);
1526 stype = item->stype;
1527 if (stype == F_NORMAL) {
1528 if (folder_has_parent_of_type(item, F_TRASH))
1530 else if (folder_has_parent_of_type(item, F_DRAFT))
1532 else if (folder_has_parent_of_type(item, F_OUTBOX))
1534 else if (folder_has_parent_of_type(item, F_QUEUE))
1539 if (item->hide_read_msgs) {
1540 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1541 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1542 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1543 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1545 xpm = mark?m_inboxxpm:inboxxpm;
1546 mask = mark?m_inboxxpmmask:inboxxpmmask;
1547 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1548 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1552 if (item->hide_read_msgs) {
1553 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1554 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1555 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1556 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1558 xpm = mark?m_outboxxpm:outboxxpm;
1559 mask = mark?m_outboxxpmmask:outboxxpmmask;
1560 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1561 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1565 if (item->hide_read_msgs) {
1566 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1567 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1568 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1569 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1571 xpm = mark?m_queuexpm:queuexpm;
1572 mask = mark?m_queuexpmmask:queuexpmmask;
1573 openxpm = mark?m_queueopenxpm:queueopenxpm;
1574 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1578 if (item->hide_read_msgs) {
1579 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1580 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1581 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1582 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1584 xpm = mark?m_trashxpm:trashxpm;
1585 mask = mark?m_trashxpmmask:trashxpmmask;
1586 openxpm = mark?m_trashopenxpm:trashopenxpm;
1587 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1591 xpm = mark?m_draftsxpm:draftsxpm;
1592 mask = mark?m_draftsxpmmask:draftsxpmmask;
1593 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1594 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1597 if (item->hide_read_msgs) {
1598 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1599 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1600 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1601 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1603 xpm = mark?m_folderxpm:folderxpm;
1604 mask = mark?m_folderxpmmask:folderxpmmask;
1605 openxpm = mark?m_folderopenxpm:folderopenxpm;
1606 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1610 if (item->no_select) {
1611 xpm = openxpm = noselectxpm;
1612 mask = openmask = noselectxpmmask;
1615 name = folder_item_get_name(item);
1617 if (!GTK_CTREE_ROW(node)->expanded) {
1618 add_unread_mark = folderview_have_unread_children(
1620 add_sub_match_mark = folderview_have_matching_children(
1623 add_unread_mark = FALSE;
1624 add_sub_match_mark = FALSE;
1627 if (item->search_match) {
1629 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1630 &searchicon, &searchmask);
1632 xpm = openxpm = searchicon;
1633 mask = openmask = searchmask;
1637 if (prefs_common.display_folder_unread) {
1638 if (folder_has_parent_of_type(item, F_QUEUE)) {
1639 /* only total_msgs matters here */
1640 if (item->total_msgs > 0) {
1641 /* show total number (should be equal to the unread number)
1643 str = g_strdup_printf("%s (%d%s%s)",
1644 name, item->total_msgs,
1645 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1646 (item->unreadmarked_msgs > 0) ? "!" : "");
1649 if (prefs_common.display_folder_unread == 1) {
1650 if (item->unread_msgs > 0) {
1651 /* show unread number and signs */
1652 str = g_strdup_printf("%s (%d%s%s)",
1653 name, item->unread_msgs,
1654 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1655 (item->unreadmarked_msgs > 0) ? "!" : "");
1658 if (item->total_msgs > 0) {
1659 /* show unread number, total number and signs if any */
1660 str = g_strdup_printf("%s (%d/%d%s%s)",
1661 name, item->unread_msgs, item->total_msgs,
1662 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1663 (item->unreadmarked_msgs > 0) ? "!" : "");
1667 if ((str == NULL) &&
1668 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1669 /* no unread/total numbers, but at least one sign */
1670 str = g_strdup_printf("%s (%s%s)",
1672 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1673 (item->unreadmarked_msgs > 0) ? "!" : "");
1677 /* last fallback, folder name only or with ! sign */
1678 str = g_strdup_printf("%s%s",
1679 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1681 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1682 xpm, mask, openxpm, openmask,
1683 FALSE, GTK_CTREE_ROW(node)->expanded);
1687 if (!folder_item_parent(item)) {
1688 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1689 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1690 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1692 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1693 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1694 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1697 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1698 folder_has_parent_of_type(item, F_DRAFT) ||
1699 folder_has_parent_of_type(item, F_TRASH)) {
1700 use_bold = use_color = FALSE;
1701 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1702 /* highlight queue folder if there are any messages */
1703 use_bold = use_color = (item->total_msgs > 0);
1705 /* if unread messages exist, print with bold font */
1706 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1708 /* if new messages exist, print with colored letter */
1710 (item->new_msgs > 0) ||
1712 folderview_have_new_children(folderview, item));
1715 gtk_ctree_node_set_foreground(ctree, node, NULL);
1720 if (item->prefs->color > 0 && !use_color) {
1721 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1722 color_style = gtk_style_copy(bold_style);
1723 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1724 style = color_style;
1725 } else if (use_color) {
1726 style = bold_color_style;
1729 if (item->op_count > 0) {
1730 style = bold_tgtfold_style;
1732 } else if (use_color) {
1733 style = normal_color_style;
1734 gtk_ctree_node_set_foreground(ctree, node,
1735 &folderview->color_new);
1736 } else if (item->op_count > 0) {
1737 style = bold_tgtfold_style;
1738 } else if (item->prefs->color > 0) {
1740 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1741 color_style = gtk_style_copy(normal_style);
1742 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1743 style = color_style;
1745 style = normal_style;
1748 gtk_ctree_node_set_row_style(ctree, node, style);
1750 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1751 folderview_update_node(folderview, node);
1754 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1757 FolderView *folderview;
1761 g_return_if_fail(item != NULL);
1763 for (list = folderview_list; list != NULL; list = list->next) {
1764 folderview = (FolderView *)list->data;
1765 ctree = GTK_CTREE(folderview->ctree);
1767 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1769 item->search_match = matches;
1770 folderview_update_node(folderview, node);
1775 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1777 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1778 FolderView *folderview = (FolderView *)data;
1781 g_return_val_if_fail(update_info != NULL, TRUE);
1782 g_return_val_if_fail(update_info->item != NULL, TRUE);
1783 g_return_val_if_fail(folderview != NULL, FALSE);
1785 ctree = GTK_CTREE(folderview->ctree);
1787 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1790 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1791 folderview_update_node(folderview, node);
1793 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1794 update_info->item == folderview->summaryview->folder_item &&
1795 update_info->item != NULL)
1796 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1797 summary_show(folderview->summaryview, update_info->item);
1803 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1804 GNode *gnode, GtkCTreeNode *cnode,
1807 FolderView *folderview = (FolderView *)data;
1808 FolderItem *item = FOLDER_ITEM(gnode->data);
1810 g_return_val_if_fail(item != NULL, FALSE);
1812 gtk_ctree_node_set_row_data(ctree, cnode, item);
1813 folderview_update_node(folderview, cnode);
1818 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1821 FolderView *folderview = (FolderView *)data;
1824 if (GTK_CTREE_ROW(node)->children) {
1825 item = gtk_ctree_node_get_row_data(ctree, node);
1826 g_return_if_fail(item != NULL);
1828 if (!item->collapsed)
1829 gtk_ctree_expand(ctree, node);
1831 folderview_update_node(folderview, node);
1835 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1836 GtkCTreeNode *root, GtkCTreeNode **prev)
1839 GtkCTreeNode *node, *parent, *sibling;
1841 node = gtk_ctree_find_by_row_data(ctree, root, item);
1843 g_warning("%s not found.\n", item->path);
1845 parent = GTK_CTREE_ROW(node)->parent;
1846 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1847 sibling = GTK_CTREE_ROW(*prev)->sibling;
1849 sibling = GTK_CTREE_ROW(parent)->children;
1853 tmp = gtk_ctree_node_get_row_data
1855 if (tmp->stype != F_NORMAL)
1856 sibling = GTK_CTREE_ROW(sibling)->sibling;
1860 if (node != sibling)
1861 gtk_ctree_move(ctree, node, parent, sibling);
1868 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1871 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1872 GtkCTreeNode *prev = NULL;
1874 gtk_clist_freeze(GTK_CLIST(ctree));
1875 gtk_sctree_sort_recursive(ctree, root);
1876 if (root && GTK_CTREE_ROW(root)->parent) {
1877 gtk_clist_thaw(GTK_CLIST(ctree));
1880 set_special_folder(ctree, folder->inbox, root, &prev);
1881 set_special_folder(ctree, folder->outbox, root, &prev);
1882 set_special_folder(ctree, folder->draft, root, &prev);
1883 set_special_folder(ctree, folder->queue, root, &prev);
1884 set_special_folder(ctree, folder->trash, root, &prev);
1885 gtk_clist_thaw(GTK_CLIST(ctree));
1888 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1890 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1893 g_return_if_fail(folder != NULL);
1895 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1896 folderview_gnode_func, folderview);
1897 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1899 folderview_sort_folders(folderview, root, folder);
1902 /* callback functions */
1903 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1904 GdkEventButton *event)
1908 FolderViewPopup *fpopup;
1909 GtkItemFactory *fpopup_factory;
1911 FolderItem *special_trash = NULL, *special_queue = NULL;
1914 item = folderview_get_selected_item(folderview);
1916 g_return_if_fail(item != NULL);
1917 g_return_if_fail(item->folder != NULL);
1918 folder = item->folder;
1920 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1922 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1924 fpopup = g_hash_table_lookup(folderview_popups, "common");
1925 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1928 if (fpopup->set_sensitivity != NULL)
1929 fpopup->set_sensitivity(fpopup_factory, item);
1931 if (NULL != (ac = account_find_from_item(item))) {
1932 special_trash = account_get_special_folder(ac, F_TRASH);
1933 special_queue = account_get_special_folder(ac, F_QUEUE);
1936 if ((item == folder->trash || item == special_trash
1937 || folder_has_parent_of_type(item, F_TRASH)) &&
1938 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1939 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1940 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1941 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1942 && !folder_has_parent_of_type(item, F_TRASH)) {
1943 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1944 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1947 if ((item == folder->queue || item == special_queue
1948 || folder_has_parent_of_type(item, F_QUEUE)) &&
1949 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1950 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1951 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1952 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1953 && !folder_has_parent_of_type(item, F_QUEUE)) {
1954 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1955 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1958 #define SET_SENS(name, sens) \
1959 menu_set_sensitive(fpopup_factory, name, sens)
1961 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1962 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1963 folderview->selected == folderview->opened);
1964 SET_SENS("/Properties...", TRUE);
1966 SET_SENS("/Run processing rules", item->prefs->processing &&
1967 item->total_msgs >= 1 && !item->processing_pending);
1968 SET_SENS("/Processing...", item->node->parent != NULL &&
1969 !item->no_select && !item->processing_pending);
1971 if (item == folder->trash || item == special_trash
1972 || folder_has_parent_of_type(item, F_TRASH)) {
1973 GSList *msglist = folder_item_get_msg_list(item);
1974 SET_SENS("/Empty trash...", msglist != NULL);
1975 procmsg_msg_list_free(msglist);
1977 if (item == folder->queue || item == special_queue
1978 || folder_has_parent_of_type(item, F_QUEUE)) {
1979 GSList *msglist = folder_item_get_msg_list(item);
1980 SET_SENS("/Send queue...", msglist != NULL);
1981 procmsg_msg_list_free(msglist);
1985 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1986 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1987 event->button, event->time);
1990 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1991 FolderView *folderview)
1993 GtkCList *clist = GTK_CLIST(ctree);
1994 gint prev_row = -1, row = -1, column = -1;
1996 if (!event) return FALSE;
1998 if (event->button == 1 || event->button == 2) {
1999 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2000 folderview->open_folder = TRUE;
2002 if (event->type == GDK_2BUTTON_PRESS) {
2003 if (clist->selection) {
2006 node = GTK_CTREE_NODE(clist->selection->data);
2008 gtk_ctree_toggle_expansion(
2011 folderview->open_folder = FALSE;
2018 if (event->button == 2 || event->button == 3) {
2020 if (clist->selection) {
2023 node = GTK_CTREE_NODE(clist->selection->data);
2025 prev_row = gtkut_ctree_get_nth_from_node
2026 (GTK_CTREE(ctree), node);
2029 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
2032 if (prev_row != row) {
2033 gtk_clist_unselect_all(clist);
2034 if (event->button == 2)
2035 folderview_select_node
2037 gtk_ctree_node_nth(GTK_CTREE(ctree),
2040 gtk_clist_select_row(clist, row, column);
2044 if (event->button != 3) return FALSE;
2046 folderview_set_sens_and_popup_menu(folderview, row, event);
2050 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2051 FolderView *folderview)
2053 int row = -1, column = -1;
2055 if (!event) return FALSE;
2057 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2060 if (event->button == 1 && folderview->open_folder == FALSE &&
2061 folderview->opened != NULL) {
2062 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2063 folderview->opened);
2064 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2070 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2071 FolderView *folderview)
2073 if (!event) return FALSE;
2075 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2078 switch (event->keyval) {
2080 #ifndef GENERIC_UMPC
2084 if (folderview->selected) {
2085 folderview_select_node(folderview,
2086 folderview->selected);
2091 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2092 gtk_ctree_toggle_expansion(
2093 GTK_CTREE(folderview->ctree),
2094 folderview->selected);
2099 if (folderview->selected) {
2100 if (folderview->opened == folderview->selected &&
2101 (!folderview->summaryview->folder_item ||
2102 folderview->summaryview->folder_item->total_msgs == 0))
2103 folderview_select_next_unread(folderview, TRUE);
2105 folderview_select_node(folderview,
2106 folderview->selected);
2116 typedef struct _PostponedSelectData
2121 FolderView *folderview;
2122 } PostponedSelectData;
2124 static gboolean postpone_select(void *data)
2126 PostponedSelectData *psdata = (PostponedSelectData *)data;
2127 debug_print("trying again\n");
2128 psdata->folderview->open_folder = TRUE;
2129 main_window_cursor_normal(psdata->folderview->mainwin);
2130 STATUSBAR_POP(psdata->folderview->mainwin);
2131 folderview_selected(psdata->ctree, psdata->row,
2132 psdata->column, psdata->folderview);
2137 void folderview_close_opened(FolderView *folderview)
2139 if (folderview->opened) {
2140 FolderItem *olditem;
2142 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2143 folderview->opened);
2145 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2146 olditem->path ? olditem->path:olditem->name);
2147 /* will be null if we just moved the previously opened folder */
2148 STATUSBAR_PUSH(folderview->mainwin, buf);
2149 main_window_cursor_wait(folderview->mainwin);
2151 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2152 summary_show(folderview->summaryview, NULL);
2153 folder_item_close(olditem);
2154 main_window_cursor_normal(folderview->mainwin);
2155 STATUSBAR_POP(folderview->mainwin);
2156 if (olditem->folder->klass->item_closed)
2157 olditem->folder->klass->item_closed(olditem);
2162 if (folderview->opened &&
2163 !GTK_CTREE_ROW(folderview->opened)->children)
2164 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2166 folderview->opened = NULL;
2168 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2169 gint column, FolderView *folderview)
2171 static gboolean can_select = TRUE; /* exclusive lock */
2176 GtkCTreeNode *old_opened = folderview->opened;
2178 folderview->selected = row;
2180 debug_print("newly selected %p, opened %p\n", folderview->selected,
2181 folderview->opened);
2182 if (folderview->opened == row) {
2183 folderview->open_folder = FALSE;
2188 item = gtk_ctree_node_get_row_data(ctree, row);
2191 folderview->open_folder = FALSE;
2195 if (!can_select || summary_is_locked(folderview->summaryview)) {
2196 if (folderview->opened) {
2197 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2198 gtk_ctree_select(ctree, folderview->opened);
2200 folderview->open_folder = FALSE;
2205 if (!folderview->open_folder) {
2212 /* Save cache for old folder */
2213 /* We don't want to lose all caches if sylpheed crashed */
2214 /* resets folderview->opened to NULL */
2215 folderview_close_opened(folderview);
2217 /* CLAWS: set compose button type: news folder items
2218 * always have a news folder as parent */
2220 toolbar_set_compose_button
2221 (folderview->mainwin->toolbar,
2222 FOLDER_TYPE(item->folder) == F_NEWS ?
2223 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2226 debug_print("Folder %s is selected\n", item->path);
2228 if (!GTK_CTREE_ROW(row)->children)
2229 gtk_ctree_expand(ctree, row);
2231 /* ungrab the mouse event */
2232 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2233 gtk_grab_remove(GTK_WIDGET(ctree));
2234 if (gdk_pointer_is_grabbed())
2235 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2239 /* TODO: wwp: avoid displaying (null) in the status bar */
2240 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2241 item->path : "(null)");
2242 debug_print("%s\n", buf);
2243 STATUSBAR_PUSH(folderview->mainwin, buf);
2246 main_window_cursor_wait(folderview->mainwin);
2248 if (folderview->scanning_folder == item->folder) {
2251 res = folder_item_open(item);
2254 if (res == -1 && item->no_select == FALSE) {
2255 main_window_cursor_normal(folderview->mainwin);
2256 STATUSBAR_POP(folderview->mainwin);
2258 alertpanel_error(_("Folder could not be opened."));
2260 folderview->open_folder = FALSE;
2264 } else if (res == -2 && item->no_select == FALSE) {
2265 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2266 data->ctree = ctree;
2268 data->column = column;
2269 data->folderview = folderview;
2270 debug_print("postponing open of %s till end of scan\n",
2271 item->path ? item->path:item->name);
2272 folderview->open_folder = FALSE;
2274 g_timeout_add(500, postpone_select, data);
2279 main_window_cursor_normal(folderview->mainwin);
2282 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2283 opened = summary_show(folderview->summaryview, item);
2285 folder_clean_cache_memory(item);
2288 gtkut_ctree_set_focus_row(ctree, old_opened);
2289 gtk_ctree_select(ctree, old_opened);
2290 folderview->opened = old_opened;
2292 folderview->opened = row;
2293 if (gtk_ctree_node_is_visible(ctree, row)
2294 != GTK_VISIBILITY_FULL)
2295 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2298 STATUSBAR_POP(folderview->mainwin);
2300 folderview->open_folder = FALSE;
2305 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2306 FolderView *folderview)
2310 item = gtk_ctree_node_get_row_data(ctree, node);
2311 g_return_if_fail(item != NULL);
2312 item->collapsed = FALSE;
2313 folderview_update_node(folderview, node);
2316 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2317 FolderView *folderview)
2321 item = gtk_ctree_node_get_row_data(ctree, node);
2322 g_return_if_fail(item != NULL);
2323 item->collapsed = TRUE;
2324 folderview_update_node(folderview, node);
2327 static void folderview_popup_close(GtkMenuShell *menu_shell,
2328 FolderView *folderview)
2330 if (!folderview->opened) return;
2332 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2335 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2336 FolderView *folderview)
2338 FolderColumnType type = folderview->col_state[column].type;
2340 prefs_common.folder_col_size[type] = width;
2343 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2347 folderview_create_folder_node(folderview, item);
2349 if (!item || !item->folder || !item->folder->node)
2352 srcnode = item->folder->node;
2353 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2354 srcnode = srcnode->children;
2355 while (srcnode != NULL) {
2356 if (srcnode && srcnode->data) {
2357 FolderItem *next_item = (FolderItem*) srcnode->data;
2358 folderview_create_folder_node_recursive(folderview, next_item);
2360 srcnode = srcnode->next;
2364 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2366 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2367 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2368 GtkCTreeNode *node, *parent_node;
2369 gint *col_pos = folderview->col_pos;
2370 FolderItemUpdateData hookdata;
2372 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2373 if (parent_node == NULL)
2376 gtk_clist_freeze(GTK_CLIST(ctree));
2378 text[col_pos[F_COL_FOLDER]] = item->name;
2379 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2381 folderxpm, folderxpmmask,
2382 folderopenxpm, folderopenxpmmask,
2384 gtk_ctree_expand(ctree, parent_node);
2385 gtk_ctree_node_set_row_data(ctree, node, item);
2387 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2388 folderview_sort_folders(folderview, parent_node, item->folder);
2390 hookdata.item = item;
2391 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2392 hookdata.msg = NULL;
2393 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2395 gtk_clist_thaw(GTK_CLIST(ctree));
2398 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2401 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2403 GSList *mlist = NULL;
2405 FolderItem *special_trash = NULL;
2408 if (!folderview->selected) return;
2409 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2410 g_return_if_fail(item != NULL);
2411 g_return_if_fail(item->folder != NULL);
2413 if (NULL != (ac = account_find_from_item(item)))
2414 special_trash = account_get_special_folder(ac, F_TRASH);
2416 if (item != item->folder->trash && item != special_trash
2417 && !folder_has_parent_of_type(item, F_TRASH)) return;
2419 if (prefs_common.ask_on_clean) {
2420 if (alertpanel(_("Empty trash"),
2421 _("Delete all messages in trash?"),
2422 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2426 mlist = folder_item_get_msg_list(item);
2428 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2429 MsgInfo * msginfo = (MsgInfo *) cur->data;
2430 if (MSG_IS_LOCKED(msginfo->flags))
2432 /* is it partially received? (partial_recv isn't cached) */
2433 if (msginfo->total_size != 0 &&
2434 msginfo->size != (off_t)msginfo->total_size)
2435 partial_mark_for_delete(msginfo);
2437 procmsg_msg_list_free(mlist);
2439 folder_item_remove_all_msg(item);
2442 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2445 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2447 FolderItem *special_queue = NULL;
2449 gchar *errstr = NULL;
2451 if (!folderview->selected) return;
2452 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2453 g_return_if_fail(item != NULL);
2454 g_return_if_fail(item->folder != NULL);
2456 if (NULL != (ac = account_find_from_item(item)))
2457 special_queue = account_get_special_folder(ac, F_QUEUE);
2459 if (item != item->folder->queue && item != special_queue
2460 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2462 if (procmsg_queue_is_empty(item))
2465 if (prefs_common.work_offline)
2466 if (alertpanel(_("Offline warning"),
2467 _("You're working offline. Override?"),
2468 GTK_STOCK_NO, GTK_STOCK_YES,
2469 NULL) != G_ALERTALTERNATE)
2472 /* ask for confirmation before sending queued messages only
2473 in online mode and if there is at least one message queued
2474 in any of the folder queue
2476 if (prefs_common.confirm_send_queued_messages) {
2477 if (!prefs_common.work_offline) {
2478 if (alertpanel(_("Send queued messages"),
2479 _("Send all queued messages?"),
2480 GTK_STOCK_CANCEL, _("_Send"),
2481 NULL) != G_ALERTALTERNATE)
2486 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2488 alertpanel_error_log(_("Some errors occurred while "
2489 "sending queued messages."));
2491 alertpanel_error_log(_("Some errors occurred "
2492 "while sending queued messages:\n%s"), errstr);
2498 static void folderview_search_cb(FolderView *folderview, guint action,
2501 summary_search(folderview->summaryview);
2504 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2507 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2510 if (!folderview->selected) return;
2512 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2513 g_return_if_fail(item != NULL);
2514 g_return_if_fail(item->folder != NULL);
2516 item->processing_pending = TRUE;
2517 folder_item_apply_processing(item);
2518 item->processing_pending = FALSE;
2521 static void folderview_property_cb(FolderView *folderview, guint action,
2524 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2527 if (!folderview->selected) return;
2529 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2530 g_return_if_fail(item != NULL);
2531 g_return_if_fail(item->folder != NULL);
2533 prefs_folder_item_open(item);
2536 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2538 GSList *list = NULL;
2539 GSList *done = NULL;
2540 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2542 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2543 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2544 && list->data != node) {
2545 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2546 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2549 for (list = done; list != NULL; list = g_slist_next(list)) {
2550 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2556 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2557 FolderItem *to_folder, gboolean copy)
2559 FolderItem *from_parent = NULL;
2560 FolderItem *new_folder = NULL;
2561 GtkCTreeNode *src_node = NULL;
2565 g_return_if_fail(folderview != NULL);
2566 g_return_if_fail(from_folder != NULL);
2567 g_return_if_fail(to_folder != NULL);
2569 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2570 from_parent = folder_item_parent(from_folder);
2572 if (prefs_common.warn_dnd) {
2573 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2574 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2575 from_folder->name, to_folder->name);
2576 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2577 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2578 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2581 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2583 else if (status & G_ALERTDISABLE)
2584 prefs_common.warn_dnd = FALSE;
2587 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2588 from_folder->name, to_folder->name);
2589 STATUSBAR_PUSH(folderview->mainwin, buf);
2591 summary_clear_all(folderview->summaryview);
2592 folderview->opened = NULL;
2593 folderview->selected = NULL;
2594 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2596 main_window_cursor_wait(folderview->mainwin);
2598 statusbar_verbosity_set(FALSE);
2599 folder_item_update_freeze();
2600 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2601 statusbar_verbosity_set(FALSE);
2602 main_window_cursor_normal(folderview->mainwin);
2603 STATUSBAR_POP(folderview->mainwin);
2604 folder_item_update_thaw();
2605 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2607 folderview_sort_folders(folderview,
2608 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2609 NULL, to_folder), new_folder->folder);
2610 folderview_select(folderview, new_folder);
2612 statusbar_verbosity_set(FALSE);
2613 main_window_cursor_normal(folderview->mainwin);
2614 STATUSBAR_POP(folderview->mainwin);
2615 folder_item_update_thaw();
2617 case F_MOVE_FAILED_DEST_IS_PARENT:
2618 alertpanel_error(_("Source and destination are the same."));
2620 case F_MOVE_FAILED_DEST_IS_CHILD:
2621 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2622 _("Can't move a folder to one of its children."));
2624 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2625 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2628 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2633 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2636 static gint folderview_clist_compare(GtkCList *clist,
2637 gconstpointer ptr1, gconstpointer ptr2)
2639 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2640 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2642 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2644 return item1->order - item2->order;
2647 // if only one folder has an order it comes first
2648 if (item1->order > 0)
2652 if (item2->order > 0)
2658 return (item2->name != NULL);
2662 return g_utf8_collate(item1->name, item2->name);
2665 static void folderview_processing_cb(FolderView *folderview, guint action,
2668 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2672 if (!folderview->selected) return;
2674 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2675 g_return_if_fail(item != NULL);
2676 g_return_if_fail(item->folder != NULL);
2678 id = folder_item_get_identifier(item);
2679 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2682 prefs_filtering_open(&item->prefs->processing, title,
2683 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2687 void folderview_set_target_folder_color(gint color_op)
2691 FolderView *folderview;
2693 for (list = folderview_list; list != NULL; list = list->next) {
2694 folderview = (FolderView *)list->data;
2695 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2697 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2698 folderview->color_op;
2704 static gchar *last_smallfont = NULL;
2705 static gchar *last_normalfont = NULL;
2706 static gchar *last_boldfont = NULL;
2707 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2710 g_free(last_smallfont);
2711 last_smallfont = NULL;
2712 g_free(last_normalfont);
2713 last_normalfont = NULL;
2714 g_free(last_boldfont);
2715 last_boldfont = NULL;
2718 void folderview_reflect_prefs(void)
2720 gboolean update_font = FALSE;
2721 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2722 FolderItem *item = folderview_get_selected_item(folderview);
2723 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2724 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2725 gint height = pos->value;
2727 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2728 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2729 !last_boldfont || strcmp(last_boldfont, BOLD_FONT))
2732 g_free(last_smallfont);
2733 last_smallfont = g_strdup(SMALL_FONT);
2734 g_free(last_normalfont);
2735 last_normalfont = g_strdup(NORMAL_FONT);
2736 g_free(last_boldfont);
2737 last_boldfont = g_strdup(BOLD_FONT);
2740 normal_style = normal_color_style = bold_style =
2741 bold_color_style = bold_tgtfold_style = NULL;
2743 folderview_init(folderview);
2745 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2746 folderview_column_set_titles(folderview);
2747 folderview_set_all();
2749 g_signal_handlers_block_by_func
2750 (G_OBJECT(folderview->ctree),
2751 G_CALLBACK(folderview_selected), folderview);
2754 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2755 GTK_CTREE(folderview->ctree), NULL, item);
2757 folderview_select(folderview, item);
2758 folderview->open_folder = FALSE;
2759 folderview->selected = node;
2762 g_signal_handlers_unblock_by_func
2763 (G_OBJECT(folderview->ctree),
2764 G_CALLBACK(folderview_selected), folderview);
2766 pos = gtk_scrolled_window_get_vadjustment(
2767 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2768 gtk_adjustment_set_value(pos, height);
2769 gtk_adjustment_changed(pos);
2770 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2773 static void drag_state_stop(FolderView *folderview)
2775 if (folderview->drag_timer)
2776 g_source_remove(folderview->drag_timer);
2777 folderview->drag_timer = 0;
2778 folderview->drag_node = NULL;
2781 static gint folderview_defer_expand(FolderView *folderview)
2783 if (folderview->drag_node) {
2784 folderview_recollapse_nodes(folderview, folderview->drag_node);
2785 if (folderview->drag_item->collapsed) {
2786 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2787 folderview->nodes_to_recollapse = g_slist_append
2788 (folderview->nodes_to_recollapse, folderview->drag_node);
2791 folderview->drag_item = NULL;
2792 folderview->drag_timer = 0;
2796 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2798 /* the idea is that we call drag_state_start() whenever we want expansion to
2799 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2800 * we need to call drag_state_stop() */
2801 drag_state_stop(folderview);
2802 /* request expansion */
2803 if (0 != (folderview->drag_timer = g_timeout_add
2804 (prefs_common.hover_timeout,
2805 (GtkFunction)folderview_defer_expand,
2807 folderview->drag_node = node;
2808 folderview->drag_item = item;
2811 #ifndef GENERIC_UMPC
2812 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2813 FolderView *folderview)
2815 GdkDragContext *context;
2817 g_return_if_fail(folderview != NULL);
2818 if (folderview->selected == NULL) return;
2819 if (folderview->nodes_to_recollapse)
2820 g_slist_free(folderview->nodes_to_recollapse);
2821 folderview->nodes_to_recollapse = NULL;
2822 context = gtk_drag_begin(widget, folderview->target_list,
2823 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2824 gtk_drag_set_icon_default(context);
2827 static void folderview_drag_data_get(GtkWidget *widget,
2828 GdkDragContext *drag_context,
2829 GtkSelectionData *selection_data,
2832 FolderView *folderview)
2836 gchar *source = NULL;
2837 if (info == TARGET_DUMMY) {
2838 for (cur = GTK_CLIST(folderview->ctree)->selection;
2839 cur != NULL; cur = cur->next) {
2840 item = gtk_ctree_node_get_row_data
2841 (GTK_CTREE(folderview->ctree),
2842 GTK_CTREE_NODE(cur->data));
2844 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2845 gtk_selection_data_set(selection_data,
2846 selection_data->target, 8,
2847 source, strlen(source));
2853 g_warning("unknown info %d\n", info);
2857 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2859 FolderUpdateData *hookdata;
2860 FolderView *folderview;
2864 folderview = (FolderView *) userdata;
2865 g_return_val_if_fail(hookdata != NULL, FALSE);
2866 g_return_val_if_fail(folderview != NULL, FALSE);
2868 ctree = folderview->ctree;
2869 g_return_val_if_fail(ctree != NULL, FALSE);
2871 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2872 folderview_create_folder_node(folderview, hookdata->item);
2873 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2874 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2875 NULL, folder_item_parent(hookdata->item));
2876 folderview_sort_folders(folderview, node, hookdata->folder);
2877 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2880 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2882 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2883 if (folderview->selected == node)
2884 folderview->selected = NULL;
2885 if (folderview->opened == node)
2886 folderview->opened = NULL;
2888 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2889 folderview_set(folderview);
2894 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2895 GdkDragContext *context,
2899 FolderView *folderview)
2902 FolderItem *item = NULL, *src_item = NULL;
2903 GtkCTreeNode *node = NULL;
2904 gboolean acceptable = FALSE;
2905 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2906 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2907 int height = (int)pos->page_size;
2908 int total_height = (int)pos->upper;
2909 int vpos = (int) pos->value;
2910 int offset = prefs_common.show_col_headers ? 24:0;
2912 if (gtk_clist_get_selection_info
2913 (GTK_CLIST(widget), x - offset, y - offset, &row, &column)) {
2914 GtkWidget *srcwidget;
2916 if (y > height - 24 && height + vpos < total_height) {
2917 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2918 gtk_adjustment_changed(pos);
2920 if (y < 48 && y > 0) {
2921 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2922 gtk_adjustment_changed(pos);
2925 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2926 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2927 src_item = folderview->summaryview->folder_item;
2929 srcwidget = gtk_drag_get_source_widget(context);
2930 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2931 /* comes from summaryview */
2932 /* we are copying messages, so only accept folder items that are not
2933 the source item, are no root items and can copy messages */
2934 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2935 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2936 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2938 } else if (srcwidget == folderview->ctree) {
2939 /* comes from folderview */
2940 /* we are moving folder items, only accept folders that are not
2941 the source items and can copy messages and create folder items */
2942 if (item && item->folder && src_item && src_item != item &&
2943 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2944 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2945 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2946 || item->folder == src_item->folder))
2949 /* comes from another app */
2950 /* we are adding messages, so only accept folder items that are
2951 no root items and can copy messages */
2952 if (item && item->folder && folder_item_parent(item) != NULL
2953 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2954 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2959 if (acceptable || (src_item && src_item == item))
2960 drag_state_start(folderview, node, item);
2963 g_signal_handlers_block_by_func
2965 G_CALLBACK(folderview_selected), folderview);
2966 gtk_ctree_select(GTK_CTREE(widget), node);
2967 g_signal_handlers_unblock_by_func
2969 G_CALLBACK(folderview_selected), folderview);
2970 gdk_drag_status(context,
2971 (context->actions == GDK_ACTION_COPY ?
2972 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2974 if (folderview->opened)
2975 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2976 gdk_drag_status(context, 0, time);
2982 static void folderview_drag_leave_cb(GtkWidget *widget,
2983 GdkDragContext *context,
2985 FolderView *folderview)
2987 drag_state_stop(folderview);
2988 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2991 static void free_info (gpointer stuff, gpointer data)
2996 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2997 guint time, FolderItem *item)
3000 GSList *msglist = NULL;
3001 list = uri_list_extract_filenames(data);
3002 if (!(item && item->folder && folder_item_parent(item) != NULL
3003 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3005 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3006 debug_print("item doesn't fit\n");
3010 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3011 debug_print("list is empty\n");
3014 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3015 MsgFileInfo *info = NULL;
3017 if (file_is_email((gchar *)tmp->data)) {
3018 info = g_new0(MsgFileInfo, 1);
3019 info->msginfo = NULL;
3020 info->file = (gchar *)tmp->data;
3021 msglist = g_slist_prepend(msglist, info);
3022 debug_print("file is a mail\n");
3024 debug_print("file isn't a mail\n");
3028 msglist = g_slist_reverse(msglist);
3029 folder_item_add_msgs(item, msglist, FALSE);
3030 g_slist_foreach(msglist, free_info, NULL);
3031 g_slist_free(msglist);
3032 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3034 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3036 list_free_strings(list);
3040 static void folderview_drag_received_cb(GtkWidget *widget,
3041 GdkDragContext *drag_context,
3044 GtkSelectionData *data,
3047 FolderView *folderview)
3050 FolderItem *item = NULL, *src_item;
3052 int offset = prefs_common.show_col_headers ? 24:0;
3054 if (info == TARGET_DUMMY) {
3055 drag_state_stop(folderview);
3056 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3057 /* comes from summaryview */
3058 if (gtk_clist_get_selection_info
3059 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3062 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3063 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3064 src_item = folderview->summaryview->folder_item;
3066 if (item->no_select) {
3067 alertpanel_error(_("The destination folder can only be used to "
3068 "store subfolders."));
3071 /* re-check (due to acceptable possibly set for folder moves */
3072 if (!(item && item->folder && item->path && !item->no_select &&
3073 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3076 if (item && src_item) {
3077 switch (drag_context->action) {
3078 case GDK_ACTION_COPY:
3079 summary_copy_selected_to(folderview->summaryview, item);
3080 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3082 case GDK_ACTION_MOVE:
3083 case GDK_ACTION_DEFAULT:
3085 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3086 summary_copy_selected_to(folderview->summaryview, item);
3088 summary_move_selected_to(folderview->summaryview, item);
3089 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3092 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3094 /* comes from folderview */
3096 gboolean folder_is_normal = TRUE;
3097 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3099 source = data->data + 17;
3100 if (gtk_clist_get_selection_info
3101 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0
3103 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3106 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3107 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3108 src_item = folder_find_item_from_identifier(source);
3112 src_item->stype == F_NORMAL &&
3113 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3114 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3115 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3116 !folder_has_parent_of_type(src_item, F_TRASH);
3117 if (!item || !src_item || !folder_is_normal) {
3118 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3122 folderview_move_folder(folderview, src_item, item, copy);
3123 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3125 folderview->nodes_to_recollapse = NULL;
3126 } else if (info == TARGET_MAIL_URI_LIST) {
3127 if (gtk_clist_get_selection_info
3128 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3131 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3133 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3134 debug_print("no node\n");
3137 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3139 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3140 debug_print("no item\n");
3143 folderview_finish_dnd(data->data, drag_context, time, item);
3147 static void folderview_drag_end_cb(GtkWidget *widget,
3148 GdkDragContext *drag_context,
3149 FolderView *folderview)
3151 drag_state_stop(folderview);
3152 g_slist_free(folderview->nodes_to_recollapse);
3153 folderview->nodes_to_recollapse = NULL;
3156 void folderview_register_popup(FolderViewPopup *fpopup)
3160 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3161 FolderView *folderview = folderviews->data;
3162 GtkItemFactory *factory;
3164 factory = create_ifactory(folderview, fpopup);
3165 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3167 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3170 void folderview_unregister_popup(FolderViewPopup *fpopup)
3174 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3175 FolderView *folderview = folderviews->data;
3177 g_hash_table_remove(folderview->popups, fpopup->klass);
3179 g_hash_table_remove(folderview_popups, fpopup->klass);