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 = TRUE;
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 ? TRUE : 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 = TRUE;
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 if ( FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_TYPE(item->folder) == F_IMAP )
1962 SET_SENS("/Download messages", !item->no_select);
1964 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1965 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1966 folderview->selected == folderview->opened);
1967 SET_SENS("/Properties...", TRUE);
1969 SET_SENS("/Run processing rules", item->prefs->processing &&
1970 item->total_msgs >= 1 && !item->processing_pending);
1971 SET_SENS("/Processing...", item->node->parent != NULL &&
1972 !item->no_select && !item->processing_pending);
1974 if (item == folder->trash || item == special_trash
1975 || folder_has_parent_of_type(item, F_TRASH)) {
1976 GSList *msglist = folder_item_get_msg_list(item);
1977 SET_SENS("/Empty trash...", msglist != NULL);
1978 procmsg_msg_list_free(msglist);
1980 if (item == folder->queue || item == special_queue
1981 || folder_has_parent_of_type(item, F_QUEUE)) {
1982 GSList *msglist = folder_item_get_msg_list(item);
1983 SET_SENS("/Send queue...", msglist != NULL);
1984 procmsg_msg_list_free(msglist);
1988 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1989 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1990 event->button, event->time);
1993 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1994 FolderView *folderview)
1996 GtkCList *clist = GTK_CLIST(ctree);
1997 gint prev_row = -1, row = -1, column = -1;
1999 if (!event) return FALSE;
2001 if (event->button == 1 || event->button == 2) {
2002 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2003 folderview->open_folder = TRUE;
2005 if (event->type == GDK_2BUTTON_PRESS) {
2006 if (clist->selection) {
2009 node = GTK_CTREE_NODE(clist->selection->data);
2011 gtk_ctree_toggle_expansion(
2014 folderview->open_folder = FALSE;
2021 if (event->button == 2 || event->button == 3) {
2023 if (clist->selection) {
2026 node = GTK_CTREE_NODE(clist->selection->data);
2028 prev_row = gtkut_ctree_get_nth_from_node
2029 (GTK_CTREE(ctree), node);
2032 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
2035 if (prev_row != row) {
2036 gtk_clist_unselect_all(clist);
2037 if (event->button == 2)
2038 folderview_select_node
2040 gtk_ctree_node_nth(GTK_CTREE(ctree),
2043 gtk_clist_select_row(clist, row, column);
2047 if (event->button != 3) return FALSE;
2049 folderview_set_sens_and_popup_menu(folderview, row, event);
2053 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2054 FolderView *folderview)
2056 int row = -1, column = -1;
2058 if (!event) return FALSE;
2060 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2063 if (event->button == 1 && folderview->open_folder == FALSE &&
2064 folderview->opened != NULL) {
2065 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2066 folderview->opened);
2067 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2073 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2074 FolderView *folderview)
2076 if (!event) return FALSE;
2078 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2081 switch (event->keyval) {
2083 #ifndef GENERIC_UMPC
2087 if (folderview->selected) {
2088 folderview_select_node(folderview,
2089 folderview->selected);
2094 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2095 gtk_ctree_toggle_expansion(
2096 GTK_CTREE(folderview->ctree),
2097 folderview->selected);
2102 if (folderview->selected) {
2103 if (folderview->opened == folderview->selected &&
2104 (!folderview->summaryview->folder_item ||
2105 folderview->summaryview->folder_item->total_msgs == 0))
2106 folderview_select_next_unread(folderview, TRUE);
2108 folderview_select_node(folderview,
2109 folderview->selected);
2119 typedef struct _PostponedSelectData
2124 FolderView *folderview;
2125 } PostponedSelectData;
2127 static gboolean postpone_select(void *data)
2129 PostponedSelectData *psdata = (PostponedSelectData *)data;
2130 debug_print("trying again\n");
2131 psdata->folderview->open_folder = TRUE;
2132 main_window_cursor_normal(psdata->folderview->mainwin);
2133 STATUSBAR_POP(psdata->folderview->mainwin);
2134 folderview_selected(psdata->ctree, psdata->row,
2135 psdata->column, psdata->folderview);
2140 void folderview_close_opened(FolderView *folderview)
2142 if (folderview->opened) {
2143 FolderItem *olditem;
2145 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2146 folderview->opened);
2148 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2149 olditem->path ? olditem->path:olditem->name);
2150 /* will be null if we just moved the previously opened folder */
2151 STATUSBAR_PUSH(folderview->mainwin, buf);
2152 main_window_cursor_wait(folderview->mainwin);
2154 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2155 summary_show(folderview->summaryview, NULL);
2156 folder_item_close(olditem);
2157 main_window_cursor_normal(folderview->mainwin);
2158 STATUSBAR_POP(folderview->mainwin);
2159 if (olditem->folder->klass->item_closed)
2160 olditem->folder->klass->item_closed(olditem);
2165 if (folderview->opened &&
2166 !GTK_CTREE_ROW(folderview->opened)->children)
2167 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2169 folderview->opened = NULL;
2171 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2172 gint column, FolderView *folderview)
2174 static gboolean can_select = TRUE; /* exclusive lock */
2179 GtkCTreeNode *old_opened = folderview->opened;
2181 folderview->selected = row;
2183 debug_print("newly selected %p, opened %p\n", folderview->selected,
2184 folderview->opened);
2185 if (folderview->opened == row) {
2186 folderview->open_folder = FALSE;
2191 item = gtk_ctree_node_get_row_data(ctree, row);
2194 folderview->open_folder = FALSE;
2198 if (!can_select || summary_is_locked(folderview->summaryview)) {
2199 if (folderview->opened) {
2200 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2201 gtk_ctree_select(ctree, folderview->opened);
2203 folderview->open_folder = FALSE;
2208 if (!folderview->open_folder) {
2215 /* Save cache for old folder */
2216 /* We don't want to lose all caches if sylpheed crashed */
2217 /* resets folderview->opened to NULL */
2218 folderview_close_opened(folderview);
2220 /* CLAWS: set compose button type: news folder items
2221 * always have a news folder as parent */
2223 toolbar_set_compose_button
2224 (folderview->mainwin->toolbar,
2225 FOLDER_TYPE(item->folder) == F_NEWS ?
2226 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2229 debug_print("Folder %s is selected\n", item->path);
2231 if (!GTK_CTREE_ROW(row)->children)
2232 gtk_ctree_expand(ctree, row);
2234 /* ungrab the mouse event */
2235 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2236 gtk_grab_remove(GTK_WIDGET(ctree));
2237 if (gdk_pointer_is_grabbed())
2238 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2242 /* TODO: wwp: avoid displaying (null) in the status bar */
2243 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2244 item->path : "(null)");
2245 debug_print("%s\n", buf);
2246 STATUSBAR_PUSH(folderview->mainwin, buf);
2249 main_window_cursor_wait(folderview->mainwin);
2251 if (folderview->scanning_folder == item->folder) {
2254 res = folder_item_open(item);
2257 if (res == -1 && item->no_select == FALSE) {
2258 main_window_cursor_normal(folderview->mainwin);
2259 STATUSBAR_POP(folderview->mainwin);
2261 alertpanel_error(_("Folder could not be opened."));
2263 folderview->open_folder = FALSE;
2267 } else if (res == -2 && item->no_select == FALSE) {
2268 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2269 data->ctree = ctree;
2271 data->column = column;
2272 data->folderview = folderview;
2273 debug_print("postponing open of %s till end of scan\n",
2274 item->path ? item->path:item->name);
2275 folderview->open_folder = FALSE;
2277 g_timeout_add(500, postpone_select, data);
2282 main_window_cursor_normal(folderview->mainwin);
2285 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2286 opened = summary_show(folderview->summaryview, item);
2288 folder_clean_cache_memory(item);
2291 gtkut_ctree_set_focus_row(ctree, old_opened);
2292 gtk_ctree_select(ctree, old_opened);
2293 folderview->opened = old_opened;
2295 folderview->opened = row;
2296 if (gtk_ctree_node_is_visible(ctree, row)
2297 != GTK_VISIBILITY_FULL)
2298 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2301 STATUSBAR_POP(folderview->mainwin);
2303 folderview->open_folder = FALSE;
2308 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2309 FolderView *folderview)
2313 item = gtk_ctree_node_get_row_data(ctree, node);
2314 g_return_if_fail(item != NULL);
2315 item->collapsed = FALSE;
2316 folderview_update_node(folderview, node);
2319 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2320 FolderView *folderview)
2324 item = gtk_ctree_node_get_row_data(ctree, node);
2325 g_return_if_fail(item != NULL);
2326 item->collapsed = TRUE;
2327 folderview_update_node(folderview, node);
2330 static void folderview_popup_close(GtkMenuShell *menu_shell,
2331 FolderView *folderview)
2333 if (!folderview->opened) return;
2335 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2338 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2339 FolderView *folderview)
2341 FolderColumnType type = folderview->col_state[column].type;
2343 prefs_common.folder_col_size[type] = width;
2346 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2350 folderview_create_folder_node(folderview, item);
2352 if (!item || !item->folder || !item->folder->node)
2355 srcnode = item->folder->node;
2356 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2357 srcnode = srcnode->children;
2358 while (srcnode != NULL) {
2359 if (srcnode && srcnode->data) {
2360 FolderItem *next_item = (FolderItem*) srcnode->data;
2361 folderview_create_folder_node_recursive(folderview, next_item);
2363 srcnode = srcnode->next;
2367 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2369 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2370 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2371 GtkCTreeNode *node, *parent_node;
2372 gint *col_pos = folderview->col_pos;
2373 FolderItemUpdateData hookdata;
2375 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2376 if (parent_node == NULL)
2379 gtk_clist_freeze(GTK_CLIST(ctree));
2381 text[col_pos[F_COL_FOLDER]] = item->name;
2382 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2384 folderxpm, folderxpmmask,
2385 folderopenxpm, folderopenxpmmask,
2387 gtk_ctree_expand(ctree, parent_node);
2388 gtk_ctree_node_set_row_data(ctree, node, item);
2390 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2391 folderview_sort_folders(folderview, parent_node, item->folder);
2393 hookdata.item = item;
2394 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2395 hookdata.msg = NULL;
2396 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2398 gtk_clist_thaw(GTK_CLIST(ctree));
2401 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2404 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2406 GSList *mlist = NULL;
2408 FolderItem *special_trash = NULL;
2411 if (!folderview->selected) return;
2412 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2413 g_return_if_fail(item != NULL);
2414 g_return_if_fail(item->folder != NULL);
2416 if (NULL != (ac = account_find_from_item(item)))
2417 special_trash = account_get_special_folder(ac, F_TRASH);
2419 if (item != item->folder->trash && item != special_trash
2420 && !folder_has_parent_of_type(item, F_TRASH)) return;
2422 if (prefs_common.ask_on_clean) {
2423 if (alertpanel(_("Empty trash"),
2424 _("Delete all messages in trash?"),
2425 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2429 mlist = folder_item_get_msg_list(item);
2431 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2432 MsgInfo * msginfo = (MsgInfo *) cur->data;
2433 if (MSG_IS_LOCKED(msginfo->flags))
2435 /* is it partially received? (partial_recv isn't cached) */
2436 if (msginfo->total_size != 0 &&
2437 msginfo->size != (off_t)msginfo->total_size)
2438 partial_mark_for_delete(msginfo);
2440 procmsg_msg_list_free(mlist);
2442 folder_item_remove_all_msg(item);
2445 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2448 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2450 FolderItem *special_queue = NULL;
2452 gchar *errstr = NULL;
2454 if (!folderview->selected) return;
2455 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2456 g_return_if_fail(item != NULL);
2457 g_return_if_fail(item->folder != NULL);
2459 if (NULL != (ac = account_find_from_item(item)))
2460 special_queue = account_get_special_folder(ac, F_QUEUE);
2462 if (item != item->folder->queue && item != special_queue
2463 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2465 if (procmsg_queue_is_empty(item))
2468 if (prefs_common.work_offline)
2469 if (alertpanel(_("Offline warning"),
2470 _("You're working offline. Override?"),
2471 GTK_STOCK_NO, GTK_STOCK_YES,
2472 NULL) != G_ALERTALTERNATE)
2475 /* ask for confirmation before sending queued messages only
2476 in online mode and if there is at least one message queued
2477 in any of the folder queue
2479 if (prefs_common.confirm_send_queued_messages) {
2480 if (!prefs_common.work_offline) {
2481 if (alertpanel(_("Send queued messages"),
2482 _("Send all queued messages?"),
2483 GTK_STOCK_CANCEL, _("_Send"),
2484 NULL) != G_ALERTALTERNATE)
2489 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2491 alertpanel_error_log(_("Some errors occurred while "
2492 "sending queued messages."));
2494 alertpanel_error_log(_("Some errors occurred "
2495 "while sending queued messages:\n%s"), errstr);
2501 static void folderview_search_cb(FolderView *folderview, guint action,
2504 summary_search(folderview->summaryview);
2507 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2510 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2513 if (!folderview->selected) return;
2515 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2516 g_return_if_fail(item != NULL);
2517 g_return_if_fail(item->folder != NULL);
2519 item->processing_pending = TRUE;
2520 folder_item_apply_processing(item);
2521 item->processing_pending = FALSE;
2524 static void folderview_property_cb(FolderView *folderview, guint action,
2527 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2530 if (!folderview->selected) return;
2532 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2533 g_return_if_fail(item != NULL);
2534 g_return_if_fail(item->folder != NULL);
2536 prefs_folder_item_open(item);
2539 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2541 GSList *list = NULL;
2542 GSList *done = NULL;
2543 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2545 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2546 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2547 && list->data != node) {
2548 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2549 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2552 for (list = done; list != NULL; list = g_slist_next(list)) {
2553 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2559 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2560 FolderItem *to_folder, gboolean copy)
2562 FolderItem *from_parent = NULL;
2563 FolderItem *new_folder = NULL;
2564 GtkCTreeNode *src_node = NULL;
2568 g_return_if_fail(folderview != NULL);
2569 g_return_if_fail(from_folder != NULL);
2570 g_return_if_fail(to_folder != NULL);
2572 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2573 from_parent = folder_item_parent(from_folder);
2575 if (prefs_common.warn_dnd) {
2576 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2577 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2578 from_folder->name, to_folder->name);
2579 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2580 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2581 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2584 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2586 else if (status & G_ALERTDISABLE)
2587 prefs_common.warn_dnd = FALSE;
2590 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2591 from_folder->name, to_folder->name);
2592 STATUSBAR_PUSH(folderview->mainwin, buf);
2594 summary_clear_all(folderview->summaryview);
2595 folderview->opened = NULL;
2596 folderview->selected = NULL;
2597 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2599 main_window_cursor_wait(folderview->mainwin);
2601 statusbar_verbosity_set(FALSE);
2602 folder_item_update_freeze();
2603 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2604 statusbar_verbosity_set(FALSE);
2605 main_window_cursor_normal(folderview->mainwin);
2606 STATUSBAR_POP(folderview->mainwin);
2607 folder_item_update_thaw();
2608 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2610 folderview_sort_folders(folderview,
2611 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2612 NULL, to_folder), new_folder->folder);
2613 folderview_select(folderview, new_folder);
2615 statusbar_verbosity_set(FALSE);
2616 main_window_cursor_normal(folderview->mainwin);
2617 STATUSBAR_POP(folderview->mainwin);
2618 folder_item_update_thaw();
2620 case F_MOVE_FAILED_DEST_IS_PARENT:
2621 alertpanel_error(_("Source and destination are the same."));
2623 case F_MOVE_FAILED_DEST_IS_CHILD:
2624 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2625 _("Can't move a folder to one of its children."));
2627 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2628 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2631 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2636 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2639 static gint folderview_clist_compare(GtkCList *clist,
2640 gconstpointer ptr1, gconstpointer ptr2)
2642 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2643 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2645 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2647 return item1->order - item2->order;
2650 // if only one folder has an order it comes first
2651 if (item1->order > 0)
2655 if (item2->order > 0)
2661 return (item2->name != NULL);
2665 return g_utf8_collate(item1->name, item2->name);
2668 static void folderview_processing_cb(FolderView *folderview, guint action,
2671 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2675 if (!folderview->selected) return;
2677 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2678 g_return_if_fail(item != NULL);
2679 g_return_if_fail(item->folder != NULL);
2681 id = folder_item_get_identifier(item);
2682 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2685 prefs_filtering_open(&item->prefs->processing, title,
2686 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2690 void folderview_set_target_folder_color(gint color_op)
2694 FolderView *folderview;
2696 for (list = folderview_list; list != NULL; list = list->next) {
2697 folderview = (FolderView *)list->data;
2698 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2700 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2701 folderview->color_op;
2707 static gchar *last_smallfont = NULL;
2708 static gchar *last_normalfont = NULL;
2709 static gchar *last_boldfont = NULL;
2710 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2713 g_free(last_smallfont);
2714 last_smallfont = NULL;
2715 g_free(last_normalfont);
2716 last_normalfont = NULL;
2717 g_free(last_boldfont);
2718 last_boldfont = NULL;
2721 void folderview_reflect_prefs(void)
2723 gboolean update_font = FALSE;
2724 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2725 FolderItem *item = folderview_get_selected_item(folderview);
2726 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2727 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2728 gint height = pos->value;
2730 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2731 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2732 !last_boldfont || strcmp(last_boldfont, BOLD_FONT))
2735 g_free(last_smallfont);
2736 last_smallfont = g_strdup(SMALL_FONT);
2737 g_free(last_normalfont);
2738 last_normalfont = g_strdup(NORMAL_FONT);
2739 g_free(last_boldfont);
2740 last_boldfont = g_strdup(BOLD_FONT);
2743 normal_style = normal_color_style = bold_style =
2744 bold_color_style = bold_tgtfold_style = NULL;
2746 folderview_init(folderview);
2748 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2749 folderview_column_set_titles(folderview);
2750 folderview_set_all();
2752 g_signal_handlers_block_by_func
2753 (G_OBJECT(folderview->ctree),
2754 G_CALLBACK(folderview_selected), folderview);
2757 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2758 GTK_CTREE(folderview->ctree), NULL, item);
2760 folderview_select(folderview, item);
2761 folderview->open_folder = FALSE;
2762 folderview->selected = node;
2765 g_signal_handlers_unblock_by_func
2766 (G_OBJECT(folderview->ctree),
2767 G_CALLBACK(folderview_selected), folderview);
2769 pos = gtk_scrolled_window_get_vadjustment(
2770 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2771 gtk_adjustment_set_value(pos, height);
2772 gtk_adjustment_changed(pos);
2773 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2776 static void drag_state_stop(FolderView *folderview)
2778 if (folderview->drag_timer)
2779 g_source_remove(folderview->drag_timer);
2780 folderview->drag_timer = 0;
2781 folderview->drag_node = NULL;
2784 static gint folderview_defer_expand(FolderView *folderview)
2786 if (folderview->drag_node) {
2787 folderview_recollapse_nodes(folderview, folderview->drag_node);
2788 if (folderview->drag_item->collapsed) {
2789 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2790 folderview->nodes_to_recollapse = g_slist_append
2791 (folderview->nodes_to_recollapse, folderview->drag_node);
2794 folderview->drag_item = NULL;
2795 folderview->drag_timer = 0;
2799 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2801 /* the idea is that we call drag_state_start() whenever we want expansion to
2802 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2803 * we need to call drag_state_stop() */
2804 drag_state_stop(folderview);
2805 /* request expansion */
2806 if (0 != (folderview->drag_timer = g_timeout_add
2807 (prefs_common.hover_timeout,
2808 (GtkFunction)folderview_defer_expand,
2810 folderview->drag_node = node;
2811 folderview->drag_item = item;
2814 #ifndef GENERIC_UMPC
2815 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2816 FolderView *folderview)
2818 GdkDragContext *context;
2820 g_return_if_fail(folderview != NULL);
2821 if (folderview->selected == NULL) return;
2822 if (folderview->nodes_to_recollapse)
2823 g_slist_free(folderview->nodes_to_recollapse);
2824 folderview->nodes_to_recollapse = NULL;
2825 context = gtk_drag_begin(widget, folderview->target_list,
2826 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2827 gtk_drag_set_icon_default(context);
2830 static void folderview_drag_data_get(GtkWidget *widget,
2831 GdkDragContext *drag_context,
2832 GtkSelectionData *selection_data,
2835 FolderView *folderview)
2839 gchar *source = NULL;
2840 if (info == TARGET_DUMMY) {
2841 for (cur = GTK_CLIST(folderview->ctree)->selection;
2842 cur != NULL; cur = cur->next) {
2843 item = gtk_ctree_node_get_row_data
2844 (GTK_CTREE(folderview->ctree),
2845 GTK_CTREE_NODE(cur->data));
2847 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2848 gtk_selection_data_set(selection_data,
2849 selection_data->target, 8,
2850 source, strlen(source));
2856 g_warning("unknown info %d\n", info);
2860 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2862 FolderUpdateData *hookdata;
2863 FolderView *folderview;
2867 folderview = (FolderView *) userdata;
2868 g_return_val_if_fail(hookdata != NULL, FALSE);
2869 g_return_val_if_fail(folderview != NULL, FALSE);
2871 ctree = folderview->ctree;
2872 g_return_val_if_fail(ctree != NULL, FALSE);
2874 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2875 folderview_create_folder_node(folderview, hookdata->item);
2876 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2877 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2878 NULL, folder_item_parent(hookdata->item));
2879 folderview_sort_folders(folderview, node, hookdata->folder);
2880 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2883 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2885 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2886 if (folderview->selected == node)
2887 folderview->selected = NULL;
2888 if (folderview->opened == node)
2889 folderview->opened = NULL;
2891 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2892 folderview_set(folderview);
2897 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2898 GdkDragContext *context,
2902 FolderView *folderview)
2905 FolderItem *item = NULL, *src_item = NULL;
2906 GtkCTreeNode *node = NULL;
2907 gboolean acceptable = FALSE;
2908 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2909 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2910 int height = (int)pos->page_size;
2911 int total_height = (int)pos->upper;
2912 int vpos = (int) pos->value;
2913 int offset = prefs_common.show_col_headers ? 24:0;
2915 if (gtk_clist_get_selection_info
2916 (GTK_CLIST(widget), x - offset, y - offset, &row, &column)) {
2917 GtkWidget *srcwidget;
2919 if (y > height - 24 && height + vpos < total_height) {
2920 gtk_adjustment_set_value(pos, (vpos+5 > total_height ? total_height : vpos+5));
2921 gtk_adjustment_changed(pos);
2923 if (y < 48 && y > 0) {
2924 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2925 gtk_adjustment_changed(pos);
2928 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2929 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2930 src_item = folderview->summaryview->folder_item;
2932 srcwidget = gtk_drag_get_source_widget(context);
2933 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2934 /* comes from summaryview */
2935 /* we are copying messages, so only accept folder items that are not
2936 the source item, are no root items and can copy messages */
2937 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2938 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2939 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2941 } else if (srcwidget == folderview->ctree) {
2942 /* comes from folderview */
2943 /* we are moving folder items, only accept folders that are not
2944 the source items and can copy messages and create folder items */
2945 if (item && item->folder && src_item && src_item != item &&
2946 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2947 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2948 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2949 || item->folder == src_item->folder))
2952 /* comes from another app */
2953 /* we are adding messages, so only accept folder items that are
2954 no root items and can copy messages */
2955 if (item && item->folder && folder_item_parent(item) != NULL
2956 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2957 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2962 if (acceptable || (src_item && src_item == item))
2963 drag_state_start(folderview, node, item);
2966 g_signal_handlers_block_by_func
2968 G_CALLBACK(folderview_selected), folderview);
2969 gtk_ctree_select(GTK_CTREE(widget), node);
2970 g_signal_handlers_unblock_by_func
2972 G_CALLBACK(folderview_selected), folderview);
2973 gdk_drag_status(context,
2974 (context->actions == GDK_ACTION_COPY ?
2975 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2977 if (folderview->opened)
2978 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2979 gdk_drag_status(context, 0, time);
2985 static void folderview_drag_leave_cb(GtkWidget *widget,
2986 GdkDragContext *context,
2988 FolderView *folderview)
2990 drag_state_stop(folderview);
2991 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2994 static void free_info (gpointer stuff, gpointer data)
2999 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3000 guint time, FolderItem *item)
3003 GSList *msglist = NULL;
3004 list = uri_list_extract_filenames(data);
3005 if (!(item && item->folder && folder_item_parent(item) != NULL
3006 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3008 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3009 debug_print("item doesn't fit\n");
3013 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3014 debug_print("list is empty\n");
3017 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3018 MsgFileInfo *info = NULL;
3020 if (file_is_email((gchar *)tmp->data)) {
3021 info = g_new0(MsgFileInfo, 1);
3022 info->msginfo = NULL;
3023 info->file = (gchar *)tmp->data;
3024 msglist = g_slist_prepend(msglist, info);
3025 debug_print("file is a mail\n");
3027 debug_print("file isn't a mail\n");
3031 msglist = g_slist_reverse(msglist);
3032 folder_item_add_msgs(item, msglist, FALSE);
3033 g_slist_foreach(msglist, free_info, NULL);
3034 g_slist_free(msglist);
3035 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3037 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3039 list_free_strings(list);
3043 static void folderview_drag_received_cb(GtkWidget *widget,
3044 GdkDragContext *drag_context,
3047 GtkSelectionData *data,
3050 FolderView *folderview)
3053 FolderItem *item = NULL, *src_item;
3055 int offset = prefs_common.show_col_headers ? 24:0;
3057 if (info == TARGET_DUMMY) {
3058 drag_state_stop(folderview);
3059 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3060 /* comes from summaryview */
3061 if (gtk_clist_get_selection_info
3062 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3065 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3066 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3067 src_item = folderview->summaryview->folder_item;
3069 if (item->no_select) {
3070 alertpanel_error(_("The destination folder can only be used to "
3071 "store subfolders."));
3074 /* re-check (due to acceptable possibly set for folder moves */
3075 if (!(item && item->folder && item->path && !item->no_select &&
3076 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3079 if (item && src_item) {
3080 switch (drag_context->action) {
3081 case GDK_ACTION_COPY:
3082 summary_copy_selected_to(folderview->summaryview, item);
3083 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3085 case GDK_ACTION_MOVE:
3086 case GDK_ACTION_DEFAULT:
3088 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3089 summary_copy_selected_to(folderview->summaryview, item);
3091 summary_move_selected_to(folderview->summaryview, item);
3092 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3095 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3097 /* comes from folderview */
3099 gboolean folder_is_normal = TRUE;
3100 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3102 source = data->data + 17;
3103 if (gtk_clist_get_selection_info
3104 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0
3106 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3109 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3110 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3111 src_item = folder_find_item_from_identifier(source);
3115 src_item->stype == F_NORMAL &&
3116 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3117 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3118 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3119 !folder_has_parent_of_type(src_item, F_TRASH);
3120 if (!item || !src_item || !folder_is_normal) {
3121 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3125 folderview_move_folder(folderview, src_item, item, copy);
3126 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3128 folderview->nodes_to_recollapse = NULL;
3129 } else if (info == TARGET_MAIL_URI_LIST) {
3130 if (gtk_clist_get_selection_info
3131 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3134 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3136 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3137 debug_print("no node\n");
3140 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3142 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3143 debug_print("no item\n");
3146 folderview_finish_dnd(data->data, drag_context, time, item);
3150 static void folderview_drag_end_cb(GtkWidget *widget,
3151 GdkDragContext *drag_context,
3152 FolderView *folderview)
3154 drag_state_stop(folderview);
3155 g_slist_free(folderview->nodes_to_recollapse);
3156 folderview->nodes_to_recollapse = NULL;
3159 void folderview_register_popup(FolderViewPopup *fpopup)
3163 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3164 FolderView *folderview = folderviews->data;
3165 GtkItemFactory *factory;
3167 factory = create_ifactory(folderview, fpopup);
3168 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3170 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3173 void folderview_unregister_popup(FolderViewPopup *fpopup)
3177 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3178 FolderView *folderview = folderviews->data;
3180 g_hash_table_remove(folderview->popups, fpopup->klass);
3182 g_hash_table_remove(folderview_popups, fpopup->klass);