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);
266 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
267 FolderView *folderview);
268 static void folderview_drag_data_get (GtkWidget *widget,
269 GdkDragContext *drag_context,
270 GtkSelectionData *selection_data,
273 FolderView *folderview);
274 static void folderview_drag_end_cb (GtkWidget *widget,
275 GdkDragContext *drag_context,
276 FolderView *folderview);
278 static void folderview_create_folder_node (FolderView *folderview,
280 static gboolean folderview_update_folder (gpointer source,
282 static gboolean folderview_update_item_claws (gpointer source,
284 static void folderview_processing_cb(FolderView *folderview, guint action,
286 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
287 GdkEventButton *event);
289 GHashTable *folderview_popups;
291 static GtkItemFactoryEntry folderview_common_popup_entries[] =
293 {N_("/Mark all re_ad"), NULL, mark_all_read_cb, 0, NULL},
294 {"/---", NULL, NULL, 0, "<Separator>"},
295 {N_("/R_un processing rules"), NULL, folderview_run_processing_cb, 0, NULL},
296 {N_("/_Search folder..."), NULL, folderview_search_cb, 0, NULL},
297 {N_("/_Properties..."), NULL, folderview_property_cb, 0, NULL},
298 {N_("/Process_ing..."), NULL, folderview_processing_cb, 0, NULL},
301 static GtkItemFactoryEntry folder_view_trash_popup_entries[] = {
302 {"/------trashsep", NULL, NULL, 0, "<Separator>"},
303 {N_("/Empty _trash..."), NULL, folderview_empty_trash_cb, 0, NULL},
306 static GtkItemFactoryEntry folder_view_queue_popup_entries[] = {
307 {"/------queuesep", NULL, NULL, 0, "<Separator>"},
308 {N_("/Send _queue..."), NULL, folderview_send_queue_cb, 0, NULL},
312 GtkTargetEntry folderview_drag_types[] =
314 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
315 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
318 void folderview_initialize(void)
320 FolderViewPopup *fpopup;
322 GSList *entries = NULL;
324 fpopup = g_new0(FolderViewPopup, 1);
326 n_entries = sizeof(folderview_common_popup_entries) /
327 sizeof(folderview_common_popup_entries[0]);
328 for (i = 0; i < n_entries; i++)
329 entries = g_slist_append(entries, &folderview_common_popup_entries[i]);
331 fpopup->klass = "common";
332 fpopup->path = "<CommonFolder>";
333 fpopup->entries = entries;
334 fpopup->set_sensitivity = NULL;
336 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
337 g_hash_table_insert(folderview_popups, "common", fpopup);
340 static GtkItemFactory *create_ifactory(FolderView *folderview, FolderViewPopup *fpopup)
343 GtkItemFactory *factory;
344 FolderViewPopup *fpopup_common;
347 factory = gtk_item_factory_new(GTK_TYPE_MENU, fpopup->path, NULL);
348 gtk_item_factory_set_translate_func(factory, menu_translate,
351 for (entries = fpopup->entries; entries != NULL; entries = g_slist_next(entries))
352 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
354 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
355 if (fpopup_common != fpopup)
356 for (entries = fpopup_common->entries; entries != NULL; entries = g_slist_next(entries))
357 gtk_item_factory_create_item(factory, entries->data, folderview, 1);
359 popup = gtk_item_factory_get_widget(factory, fpopup->path);
360 g_signal_connect(G_OBJECT(popup), "selection_done",
361 G_CALLBACK(folderview_popup_close),
367 static void create_ifactories(gpointer key, gpointer value, gpointer data)
369 FolderView *folderview = data;
370 FolderViewPopup *fpopup = value;
371 GtkItemFactory *factory;
373 factory = create_ifactory(folderview, fpopup);
374 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
377 static void folderview_column_set_titles(FolderView *folderview)
379 GtkWidget *ctree = folderview->ctree;
380 GtkWidget *label_folder;
381 GtkWidget *label_new;
382 GtkWidget *label_unread;
383 GtkWidget *label_total;
384 GtkWidget *hbox_folder;
386 GtkWidget *hbox_unread;
387 GtkWidget *hbox_total;
388 gint *col_pos = folderview->col_pos;
390 debug_print("setting titles...\n");
391 gtk_widget_realize(folderview->ctree);
392 gtk_widget_show_all(folderview->scrolledwin);
394 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
395 * instead text (text overflows making them unreadable and ugly) */
396 stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
397 &newxpm, &newxpmmask);
398 stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
399 &unreadxpm, &unreadxpmmask);
400 stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
401 &readxpm, &readxpmmask);
403 label_folder = gtk_label_new(_("Folder"));
404 label_new = gtk_image_new_from_pixmap(newxpm, newxpmmask);
405 label_unread = gtk_image_new_from_pixmap(unreadxpm, unreadxpmmask);
406 label_total = gtk_image_new_from_pixmap(readxpm, readxpmmask);
408 gtk_clist_column_titles_active(GTK_CLIST(ctree));
410 hbox_folder = gtk_hbox_new(FALSE, 4);
411 hbox_new = gtk_hbox_new(FALSE, 4);
412 hbox_unread = gtk_hbox_new(FALSE, 4);
413 hbox_total = gtk_hbox_new(FALSE, 4);
416 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
417 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
418 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
419 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
420 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
421 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
422 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
423 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
425 gtk_widget_show_all(hbox_folder);
426 gtk_widget_show_all(hbox_new);
427 gtk_widget_show_all(hbox_unread);
428 gtk_widget_show_all(hbox_total);
431 gtk_widget_set_size_request(hbox_new, -1, 20);
432 gtk_widget_set_size_request(hbox_unread, -1, 20);
433 gtk_widget_set_size_request(hbox_total, -1, 20);
436 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
437 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_NEW],hbox_new);
438 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
439 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
445 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
446 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
447 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
450 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
452 FolderView *folderview = (FolderView *)data;
453 GdkEventButton event;
454 if (folderview_get_selected_item(folderview) == NULL)
458 event.time = gtk_get_current_event_time();
460 folderview_set_sens_and_popup_menu(folderview, -1,
467 static GtkWidget *folderview_ctree_create(FolderView *folderview)
471 FolderColumnState *col_state;
472 FolderColumnType type;
473 gchar *titles[N_FOLDER_COLS];
475 GtkWidget *scrolledwin = folderview->scrolledwin;
477 debug_print("creating tree...\n");
478 memset(titles, 0, sizeof(titles));
480 col_state = prefs_folder_column_get_config();
481 memset(titles, 0, sizeof(titles));
483 col_pos = folderview->col_pos;
485 for (i = 0; i < N_FOLDER_COLS; i++) {
486 folderview->col_state[i] = col_state[i];
487 type = col_state[i].type;
491 titles[col_pos[F_COL_FOLDER]] = _("Folder");
492 titles[col_pos[F_COL_NEW]] = _("New");
493 titles[col_pos[F_COL_UNREAD]] = _("Unread");
494 /* TRANSLATORS: This in Number sign in American style */
495 titles[col_pos[F_COL_TOTAL]] = _("#");
497 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
500 if (prefs_common.show_col_headers == FALSE)
501 gtk_clist_column_titles_hide(GTK_CLIST(ctree));
504 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
505 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[F_COL_NEW],
507 gtk_clist_set_column_justification(GTK_CLIST(ctree),
508 col_pos[F_COL_UNREAD],
510 gtk_clist_set_column_justification(GTK_CLIST(ctree),
511 col_pos[F_COL_TOTAL],
513 if (prefs_common.enable_dotted_lines) {
514 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
515 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
516 GTK_CTREE_EXPANDER_SQUARE);
518 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
519 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
520 GTK_CTREE_EXPANDER_TRIANGLE);
523 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
524 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
526 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
527 gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
529 /* don't let title buttons take key focus */
530 for (i = 0; i < N_FOLDER_COLS; i++) {
531 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
533 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[i],
534 prefs_common.folder_col_size[i]);
535 gtk_clist_set_column_visibility
536 (GTK_CLIST(ctree), i, col_state[i].visible);
539 g_signal_connect(G_OBJECT(ctree), "key_press_event",
540 G_CALLBACK(folderview_key_pressed),
542 g_signal_connect(G_OBJECT(ctree), "button_press_event",
543 G_CALLBACK(folderview_button_pressed),
546 g_signal_connect(G_OBJECT(ctree), "popup-menu",
547 G_CALLBACK(folderview_popup_menu), folderview);
549 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
550 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
551 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
552 G_CALLBACK(folderview_popup_menu), folderview);
554 g_signal_connect(G_OBJECT(ctree), "button_release_event",
555 G_CALLBACK(folderview_button_released),
557 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
558 G_CALLBACK(folderview_selected), folderview);
560 /* drag-n-dropping folders on maemo is impractical as this
561 * opens the folder almost everytime */
562 g_signal_connect(G_OBJECT(ctree), "start_drag",
563 G_CALLBACK(folderview_start_drag), folderview);
565 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
566 G_CALLBACK(folderview_drag_data_get),
569 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
570 G_CALLBACK(folderview_tree_expanded),
572 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
573 G_CALLBACK(folderview_tree_collapsed),
576 g_signal_connect(G_OBJECT(ctree), "resize_column",
577 G_CALLBACK(folderview_col_resized),
581 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
582 folderview_drag_types, 2,
583 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
584 g_signal_connect(G_OBJECT(ctree), "drag_motion",
585 G_CALLBACK(folderview_drag_motion_cb),
587 g_signal_connect(G_OBJECT(ctree), "drag_leave",
588 G_CALLBACK(folderview_drag_leave_cb),
590 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
591 G_CALLBACK(folderview_drag_received_cb),
593 g_signal_connect(G_OBJECT(ctree), "drag_end",
594 G_CALLBACK(folderview_drag_end_cb),
597 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
602 void folderview_set_column_order(FolderView *folderview)
605 FolderItem *item = folderview_get_selected_item(folderview);
606 GtkWidget *scrolledwin = folderview->scrolledwin;
608 debug_print("recreating tree...\n");
609 gtk_widget_destroy(folderview->ctree);
611 folderview->ctree = ctree = folderview_ctree_create(folderview);
612 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
613 GTK_CLIST(ctree)->hadjustment);
614 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
615 GTK_CLIST(ctree)->vadjustment);
616 gtk_widget_show(ctree);
618 folderview_set(folderview);
619 folderview_column_set_titles(folderview);
621 folderview_select(folderview,item);
624 FolderView *folderview_create(void)
626 FolderView *folderview;
627 GtkWidget *scrolledwin;
630 debug_print("Creating folder view...\n");
631 folderview = g_new0(FolderView, 1);
633 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
634 gtk_scrolled_window_set_policy
635 (GTK_SCROLLED_WINDOW(scrolledwin),
636 GTK_POLICY_AUTOMATIC,
637 prefs_common.folderview_vscrollbar_policy);
638 gtk_widget_set_size_request(scrolledwin,
639 prefs_common.folderview_width,
640 prefs_common.folderview_height);
642 folderview->scrolledwin = scrolledwin;
643 ctree = folderview_ctree_create(folderview);
645 /* create popup factories */
646 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
647 g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
649 folderview->ctree = ctree;
651 folderview->folder_update_callback_id =
652 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
653 folderview->folder_item_update_callback_id =
654 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
656 gtk_widget_show_all(scrolledwin);
658 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
659 folderview_list = g_list_append(folderview_list, folderview);
660 folderview->deferred_refresh_id = -1;
665 void folderview_init(FolderView *folderview)
667 GtkWidget *ctree = folderview->ctree;
670 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
671 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
672 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
673 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
674 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
675 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
676 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
677 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
678 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
679 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
680 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
681 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
682 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
683 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
686 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
687 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
688 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
689 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
690 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
691 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
692 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
694 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
695 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
696 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
697 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
698 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
699 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
700 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
701 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
702 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
703 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
704 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
705 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
706 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
707 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
708 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
709 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
710 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
711 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
712 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
713 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
714 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
715 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
718 PangoFontDescription *font_desc;
719 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
720 font_desc = pango_font_description_from_string(NORMAL_FONT);
722 if (normal_style->font_desc)
723 pango_font_description_free
724 (normal_style->font_desc);
725 normal_style->font_desc = font_desc;
727 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
728 normal_color_style = gtk_style_copy(normal_style);
729 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
731 gtk_widget_set_style(ctree, normal_style);
735 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
736 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
737 pango_font_description_set_weight
738 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
739 bold_color_style = gtk_style_copy(bold_style);
740 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
742 bold_tgtfold_style = gtk_style_copy(bold_style);
743 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
747 static gboolean folderview_defer_set(gpointer data)
749 FolderView *folderview = (FolderView *)data;
750 MainWindow *mainwin = folderview->mainwin;
754 if (mainwin->lock_count)
757 debug_print("doing deferred folderview_set now\n");
758 folderview_set(folderview);
760 folderview->deferred_refresh_id = -1;
764 void folderview_set(FolderView *folderview)
766 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
767 MainWindow *mainwin = folderview->mainwin;
768 FolderItem *sel_item = NULL, *op_item = NULL;
773 if (mainwin->lock_count) {
774 if (folderview->deferred_refresh_id == -1)
775 folderview->deferred_refresh_id =
776 g_timeout_add(500, folderview_defer_set, folderview);
777 debug_print("deferred folderview_set\n");
782 debug_print("Setting folder info...\n");
783 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
785 main_window_cursor_wait(mainwin);
787 if (folderview->selected)
788 sel_item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
789 if (folderview->opened)
790 op_item = gtk_ctree_node_get_row_data(ctree, folderview->opened);
792 folderview->selected = NULL;
793 folderview->opened = NULL;
795 gtk_clist_freeze(GTK_CLIST(ctree));
796 gtk_clist_clear(GTK_CLIST(ctree));
798 folderview_set_folders(folderview);
801 folderview->selected = gtk_ctree_find_by_row_data(ctree, NULL, sel_item);
803 folderview->opened = gtk_ctree_find_by_row_data(ctree, NULL, op_item);
805 gtk_clist_thaw(GTK_CLIST(ctree));
806 main_window_cursor_normal(mainwin);
807 STATUSBAR_POP(mainwin);
811 void folderview_set_all(void)
815 for (list = folderview_list; list != NULL; list = list->next)
816 folderview_set((FolderView *)list->data);
819 void folderview_select(FolderView *folderview, FolderItem *item)
821 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
823 GtkCTreeNode *old_selected = folderview->selected;
827 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
828 if (node) folderview_select_node(folderview, node);
830 if (old_selected != node)
831 folder_update_op_count();
834 static void mark_all_read_cb(FolderView *folderview, guint action,
840 item = folderview_get_selected_item(folderview);
844 if (folderview->summaryview->folder_item != item
845 && prefs_common.ask_mark_all_read) {
846 val = alertpanel_full(_("Mark all as read"),
847 _("Do you really want to mark all mails in this "
848 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
849 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
851 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
853 else if (val & G_ALERTDISABLE)
854 prefs_common.ask_mark_all_read = FALSE;
857 summary_lock(folderview->summaryview);
858 folder_item_update_freeze();
859 if (folderview->summaryview->folder_item == item)
860 summary_freeze(folderview->summaryview);
861 folderutils_mark_all_read(item);
862 if (folderview->summaryview->folder_item == item)
863 summary_thaw(folderview->summaryview);
864 folder_item_update_thaw();
865 summary_unlock(folderview->summaryview);
868 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
870 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
872 g_return_if_fail(node != NULL);
874 if (folderview->open_folder) {
878 folderview->open_folder = TRUE;
879 gtkut_ctree_set_focus_row(ctree, node);
880 gtk_ctree_select(ctree, node);
881 if (folderview->summaryview->folder_item &&
882 folderview->summaryview->folder_item->total_msgs > 0)
883 summary_grab_focus(folderview->summaryview);
885 gtk_widget_grab_focus(folderview->ctree);
887 gtkut_ctree_expand_parent_all(ctree, node);
890 void folderview_unselect(FolderView *folderview)
892 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
894 (GTK_CTREE(folderview->ctree), folderview->opened);
896 folderview->selected = folderview->opened = NULL;
899 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
905 node = gtkut_ctree_node_next(ctree, node);
907 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
909 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
910 item = gtk_ctree_node_get_row_data(ctree, node);
911 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
918 void folderview_select_next_marked(FolderView *folderview)
920 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
921 GtkCTreeNode *node = NULL;
922 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
923 gboolean last_open = prefs_common.always_show_msg;
925 prefs_common.summary_select_prio[0] = ACTION_MARKED;
926 prefs_common.always_show_msg = TRUE;
928 if ((node = folderview_find_next_marked(ctree, folderview->opened))
930 folderview_select_node(folderview, node);
934 if (!folderview->opened ||
935 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
938 /* search again from the first node */
939 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
940 folderview_select_node(folderview, node);
943 prefs_common.summary_select_prio[0] = last_summary_select_prio;
944 prefs_common.always_show_msg = last_open;
947 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
953 node = gtkut_ctree_node_next(ctree, node);
955 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
957 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
958 item = gtk_ctree_node_get_row_data(ctree, node);
959 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
966 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
968 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
969 GtkCTreeNode *node = NULL;
970 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
971 gboolean last_open = prefs_common.always_show_msg;
973 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
974 prefs_common.always_show_msg = force_open ? TRUE : last_open;
976 if ((node = folderview_find_next_unread(ctree, folderview->opened))
978 folderview_select_node(folderview, node);
982 if (!folderview->opened ||
983 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
986 /* search again from the first node */
987 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
988 folderview_select_node(folderview, node);
991 prefs_common.summary_select_prio[0] = last_summary_select_prio;
992 prefs_common.always_show_msg = last_open;
995 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
1001 node = gtkut_ctree_node_next(ctree, node);
1003 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1005 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1006 item = gtk_ctree_node_get_row_data(ctree, node);
1007 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
1014 void folderview_select_next_new(FolderView *folderview)
1016 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1017 GtkCTreeNode *node = NULL;
1018 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1019 gboolean last_open = prefs_common.always_show_msg;
1021 prefs_common.summary_select_prio[0] = ACTION_NEW;
1022 prefs_common.always_show_msg = TRUE;
1024 if ((node = folderview_find_next_new(ctree, folderview->opened))
1026 folderview_select_node(folderview, node);
1030 if (!folderview->opened ||
1031 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1034 /* search again from the first node */
1035 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1036 folderview_select_node(folderview, node);
1039 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1040 prefs_common.always_show_msg = last_open;
1043 FolderItem *folderview_get_selected_item(FolderView *folderview)
1045 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1047 if (!folderview->selected) return NULL;
1048 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1051 static void folderview_set_folders(FolderView *folderview)
1054 list = folder_get_list();
1056 for (; list != NULL; list = list->next) {
1057 folderview_append_folder(folderview, FOLDER(list->data));
1061 static gchar *get_scan_str(FolderItem *item)
1064 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1065 item->folder->name, G_DIR_SEPARATOR,
1068 return g_strdup_printf(_("Scanning folder %s ..."),
1069 item->folder->name);
1071 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1075 for (list = folderview_list; list != NULL; list = list->next) {
1076 FolderView *folderview = (FolderView *)list->data;
1077 MainWindow *mainwin = folderview->mainwin;
1078 gchar *str = get_scan_str(item);
1080 STATUSBAR_PUSH(mainwin, str);
1081 STATUSBAR_POP(mainwin);
1086 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1089 MainWindow *mainwin = mainwindow_get_mainwindow();
1090 FolderView *folderview = NULL;
1091 GtkAdjustment *pos = NULL;
1094 g_return_if_fail(folder != NULL);
1096 if (!folder->klass->scan_tree) return;
1099 alertpanel_full(_("Rebuild folder tree"),
1100 _("Rebuilding the folder tree will remove "
1101 "local caches. Do you want to continue?"),
1102 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1103 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1104 != G_ALERTALTERNATE) {
1110 window = label_window_create(_("Rebuilding folder tree..."));
1112 window = label_window_create(_("Scanning folder tree..."));
1115 folderview = mainwin->folderview;
1118 pos = gtk_scrolled_window_get_vadjustment(
1119 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1120 height = pos->value;
1123 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1124 folder_scan_tree(folder, rebuild);
1125 folder_set_ui_func(folder, NULL, NULL);
1127 folderview_set_all();
1130 pos = gtk_scrolled_window_get_vadjustment(
1131 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1132 gtk_adjustment_set_value(pos, height);
1133 gtk_adjustment_changed(pos);
1135 label_window_destroy(window);
1139 void folderview_fast_rescan_tree(Folder *folder)
1142 MainWindow *mainwin = mainwindow_get_mainwindow();
1143 FolderView *folderview = NULL;
1144 GtkAdjustment *pos = NULL;
1147 g_return_if_fail(folder != NULL);
1149 if (!folder->klass->scan_tree) return;
1153 window = label_window_create(_("Scanning folder tree..."));
1156 folderview = mainwin->folderview;
1159 pos = gtk_scrolled_window_get_vadjustment(
1160 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1161 height = pos->value;
1164 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1165 folder_fast_scan_tree(folder);
1166 folder_set_ui_func(folder, NULL, NULL);
1168 folderview_set_all();
1171 pos = gtk_scrolled_window_get_vadjustment(
1172 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1173 gtk_adjustment_set_value(pos, height);
1174 gtk_adjustment_changed(pos);
1176 label_window_destroy(window);
1180 /** folderview_check_new()
1181 * Scan and update the folder and return the
1182 * count the number of new messages since last check.
1183 * \param folder the folder to check for new messages
1184 * \return the number of new messages since last check
1186 gint folderview_check_new(Folder *folder)
1190 FolderView *folderview;
1194 gint former_new_msgs = 0;
1195 gint former_new = 0, former_unread = 0, former_total;
1197 for (list = folderview_list; list != NULL; list = list->next) {
1198 folderview = (FolderView *)list->data;
1199 ctree = GTK_CTREE(folderview->ctree);
1200 folderview->scanning_folder = folder;
1202 main_window_lock(folderview->mainwin);
1204 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1205 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1207 item = gtk_ctree_node_get_row_data(ctree, node);
1208 if (!item || !item->path || !item->folder) continue;
1209 if (item->no_select) continue;
1210 if (folder && folder != item->folder) continue;
1211 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1212 if (!item->prefs->newmailcheck) continue;
1213 if (item->processing_pending == TRUE) {
1214 debug_print("skipping %s, processing pending\n",
1215 item->path ? item->path : item->name);
1218 if (item->scanning == TRUE) {
1219 debug_print("skipping %s, scanning\n",
1220 item->path ? item->path : item->name);
1224 str = get_scan_str(item);
1226 STATUSBAR_PUSH(folderview->mainwin, str);
1230 folderview_scan_tree_func(item->folder, item, NULL);
1231 former_new = item->new_msgs;
1232 former_unread = item->unread_msgs;
1233 former_total = item->total_msgs;
1235 if (item->folder->klass->scan_required &&
1236 (item->folder->klass->scan_required(item->folder, item) ||
1237 item->folder->inbox == item ||
1238 item->opened == TRUE ||
1239 item->processing_pending == TRUE)) {
1240 if (folder_item_scan(item) < 0) {
1242 summaryview_unlock(folderview->summaryview, item);
1243 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1244 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1245 item->path ? item->path:item->name);
1246 STATUSBAR_POP(folderview->mainwin);
1248 } else if (!FOLDER_IS_LOCAL(folder)) {
1249 STATUSBAR_POP(folderview->mainwin);
1254 } else if (!item->folder->klass->scan_required) {
1255 if (folder_item_scan(item) < 0) {
1256 summaryview_unlock(folderview->summaryview, item);
1257 if (folder && !FOLDER_IS_LOCAL(folder)) {
1258 STATUSBAR_POP(folderview->mainwin);
1263 if (former_new != item->new_msgs ||
1264 former_unread != item->unread_msgs ||
1265 former_total != item->total_msgs)
1266 folderview_update_node(folderview, node);
1268 new_msgs += item->new_msgs;
1269 former_new_msgs += former_new;
1270 STATUSBAR_POP(folderview->mainwin);
1272 folderview->scanning_folder = NULL;
1273 main_window_unlock(folderview->mainwin);
1277 folder_write_list();
1278 /* Number of new messages since last check is the just the difference
1279 * between former_new_msgs and new_msgs. If new_msgs is less than
1280 * former_new_msgs, that would mean another session accessed the folder
1281 * and the result is not well defined.
1283 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1287 void folderview_check_new_all(void)
1291 FolderView *folderview;
1293 folderview = (FolderView *)folderview_list->data;
1296 main_window_lock(folderview->mainwin);
1297 window = label_window_create
1298 (_("Checking for new messages in all folders..."));
1300 list = folder_get_list();
1301 for (; list != NULL; list = list->next) {
1302 Folder *folder = list->data;
1304 folderview_check_new(folder);
1307 folder_write_list();
1308 folderview_set_all();
1310 label_window_destroy(window);
1311 main_window_unlock(folderview->mainwin);
1315 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1321 if (!item || !item->folder || !item->folder->node)
1324 node = item->folder->node;
1326 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1327 node = node->children;
1330 (item->new_msgs > 0 ||
1331 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1335 while (node != NULL) {
1336 if (node && node->data) {
1337 FolderItem *next_item = (FolderItem*) node->data;
1339 if (folderview_have_new_children_sub(folderview,
1348 static gboolean folderview_have_new_children(FolderView *folderview,
1351 return folderview_have_new_children_sub(folderview, item, FALSE);
1354 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1360 if (!item || !item->folder || !item->folder->node)
1363 node = item->folder->node;
1365 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1366 node = node->children;
1369 (item->unread_msgs > 0 ||
1370 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1374 while (node != NULL) {
1375 if (node && node->data) {
1376 FolderItem *next_item = (FolderItem*) node->data;
1378 if (folderview_have_unread_children_sub(folderview,
1388 static gboolean folderview_have_unread_children(FolderView *folderview,
1391 return folderview_have_unread_children_sub(folderview, item, FALSE);
1394 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1400 if (!item || !item->folder || !item->folder->node)
1403 node = item->folder->node;
1405 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1406 node = node->children;
1408 if (in_sub && item->search_match){
1412 while (node != NULL) {
1413 if (node && node->data) {
1414 FolderItem *next_item = (FolderItem*) node->data;
1416 if (folderview_have_matching_children_sub(folderview,
1426 static gboolean folderview_have_matching_children(FolderView *folderview,
1429 return folderview_have_matching_children_sub(folderview, item, FALSE);
1432 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1438 if (!item || !item->folder || !item->folder->node)
1441 node = item->folder->node;
1443 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1444 node = node->children;
1446 if (item->marked_msgs != 0) {
1450 while (node != NULL) {
1451 if (node && node->data) {
1452 FolderItem *next_item = (FolderItem*) node->data;
1454 if (folderview_have_marked_children_sub(folderview,
1463 static gboolean folderview_have_marked_children(FolderView *folderview,
1466 return folderview_have_marked_children_sub(folderview, item, FALSE);
1469 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1471 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1472 GtkStyle *style = NULL;
1473 GtkStyle *color_style = NULL;
1475 GdkPixmap *xpm, *openxpm;
1476 GdkBitmap *mask, *openmask;
1477 static GdkPixmap *searchicon;
1478 static GdkBitmap *searchmask;
1479 gboolean mark = FALSE;
1482 gboolean add_unread_mark;
1483 gboolean add_sub_match_mark;
1484 gboolean use_bold, use_color;
1485 gint *col_pos = folderview->col_pos;
1486 SpecialFolderItemType stype;
1488 item = gtk_ctree_node_get_row_data(ctree, node);
1489 g_return_if_fail(item != NULL);
1491 if (!GTK_CTREE_ROW(node)->expanded)
1492 mark = folderview_have_marked_children(folderview, item);
1494 mark = (item->marked_msgs != 0);
1496 stype = item->stype;
1497 if (stype == F_NORMAL) {
1498 if (folder_has_parent_of_type(item, F_TRASH))
1500 else if (folder_has_parent_of_type(item, F_DRAFT))
1502 else if (folder_has_parent_of_type(item, F_OUTBOX))
1504 else if (folder_has_parent_of_type(item, F_QUEUE))
1509 if (item->hide_read_msgs) {
1510 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1511 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1512 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1513 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1515 xpm = mark?m_inboxxpm:inboxxpm;
1516 mask = mark?m_inboxxpmmask:inboxxpmmask;
1517 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1518 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1522 if (item->hide_read_msgs) {
1523 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1524 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1525 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1526 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1528 xpm = mark?m_outboxxpm:outboxxpm;
1529 mask = mark?m_outboxxpmmask:outboxxpmmask;
1530 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1531 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1535 if (item->hide_read_msgs) {
1536 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1537 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1538 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1539 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1541 xpm = mark?m_queuexpm:queuexpm;
1542 mask = mark?m_queuexpmmask:queuexpmmask;
1543 openxpm = mark?m_queueopenxpm:queueopenxpm;
1544 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1548 if (item->hide_read_msgs) {
1549 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1550 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1551 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1552 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1554 xpm = mark?m_trashxpm:trashxpm;
1555 mask = mark?m_trashxpmmask:trashxpmmask;
1556 openxpm = mark?m_trashopenxpm:trashopenxpm;
1557 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1561 xpm = mark?m_draftsxpm:draftsxpm;
1562 mask = mark?m_draftsxpmmask:draftsxpmmask;
1563 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1564 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1567 if (item->hide_read_msgs) {
1568 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1569 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1570 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1571 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1573 xpm = mark?m_folderxpm:folderxpm;
1574 mask = mark?m_folderxpmmask:folderxpmmask;
1575 openxpm = mark?m_folderopenxpm:folderopenxpm;
1576 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1580 if (item->no_select) {
1581 xpm = openxpm = noselectxpm;
1582 mask = openmask = noselectxpmmask;
1585 name = folder_item_get_name(item);
1587 if (!GTK_CTREE_ROW(node)->expanded) {
1588 add_unread_mark = folderview_have_unread_children(
1590 add_sub_match_mark = folderview_have_matching_children(
1593 add_unread_mark = FALSE;
1594 add_sub_match_mark = FALSE;
1597 if (item->search_match) {
1599 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1600 &searchicon, &searchmask);
1602 xpm = openxpm = searchicon;
1603 mask = openmask = searchmask;
1607 if (prefs_common.display_folder_unread) {
1608 if (folder_has_parent_of_type(item, F_QUEUE)) {
1609 /* only total_msgs matters here */
1610 if (item->total_msgs > 0) {
1611 /* show total number (should be equal to the unread number)
1613 str = g_strdup_printf("%s (%d%s%s)",
1614 name, item->total_msgs,
1615 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1616 (item->unreadmarked_msgs > 0) ? "!" : "");
1619 if (prefs_common.display_folder_unread == 1) {
1620 if (item->unread_msgs > 0) {
1621 /* show unread number and signs */
1622 str = g_strdup_printf("%s (%d%s%s)",
1623 name, item->unread_msgs,
1624 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1625 (item->unreadmarked_msgs > 0) ? "!" : "");
1628 if (item->total_msgs > 0) {
1629 /* show unread number, total number and signs if any */
1630 str = g_strdup_printf("%s (%d/%d%s%s)",
1631 name, item->unread_msgs, item->total_msgs,
1632 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1633 (item->unreadmarked_msgs > 0) ? "!" : "");
1637 if ((str == NULL) &&
1638 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1639 /* no unread/total numbers, but at least one sign */
1640 str = g_strdup_printf("%s (%s%s)",
1642 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1643 (item->unreadmarked_msgs > 0) ? "!" : "");
1647 /* last fallback, folder name only or with ! sign */
1648 str = g_strdup_printf("%s%s",
1649 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1651 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1652 xpm, mask, openxpm, openmask,
1653 FALSE, GTK_CTREE_ROW(node)->expanded);
1657 if (!folder_item_parent(item)) {
1658 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1659 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1660 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1662 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1663 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1664 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1667 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1668 folder_has_parent_of_type(item, F_DRAFT) ||
1669 folder_has_parent_of_type(item, F_TRASH)) {
1670 use_bold = use_color = FALSE;
1671 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1672 /* highlight queue folder if there are any messages */
1673 use_bold = use_color = (item->total_msgs > 0);
1675 /* if unread messages exist, print with bold font */
1676 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1678 /* if new messages exist, print with colored letter */
1680 (item->new_msgs > 0) ||
1682 folderview_have_new_children(folderview, item));
1685 gtk_ctree_node_set_foreground(ctree, node, NULL);
1690 if (item->prefs->color > 0 && !use_color) {
1691 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1692 color_style = gtk_style_copy(bold_style);
1693 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1694 style = color_style;
1695 } else if (use_color) {
1696 style = bold_color_style;
1699 if (item->op_count > 0) {
1700 style = bold_tgtfold_style;
1702 } else if (use_color) {
1703 style = normal_color_style;
1704 gtk_ctree_node_set_foreground(ctree, node,
1705 &folderview->color_new);
1706 } else if (item->op_count > 0) {
1707 style = bold_tgtfold_style;
1708 } else if (item->prefs->color > 0) {
1710 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1711 color_style = gtk_style_copy(normal_style);
1712 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1713 style = color_style;
1715 style = normal_style;
1718 gtk_ctree_node_set_row_style(ctree, node, style);
1720 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1721 folderview_update_node(folderview, node);
1724 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1727 FolderView *folderview;
1731 g_return_if_fail(item != NULL);
1733 for (list = folderview_list; list != NULL; list = list->next) {
1734 folderview = (FolderView *)list->data;
1735 ctree = GTK_CTREE(folderview->ctree);
1737 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1739 item->search_match = matches;
1740 folderview_update_node(folderview, node);
1745 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1747 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1748 FolderView *folderview = (FolderView *)data;
1751 g_return_val_if_fail(update_info != NULL, TRUE);
1752 g_return_val_if_fail(update_info->item != NULL, TRUE);
1753 g_return_val_if_fail(folderview != NULL, FALSE);
1755 ctree = GTK_CTREE(folderview->ctree);
1757 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1760 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1761 folderview_update_node(folderview, node);
1763 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1764 update_info->item == folderview->summaryview->folder_item &&
1765 update_info->item != NULL)
1766 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1767 summary_show(folderview->summaryview, update_info->item);
1773 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1774 GNode *gnode, GtkCTreeNode *cnode,
1777 FolderView *folderview = (FolderView *)data;
1778 FolderItem *item = FOLDER_ITEM(gnode->data);
1780 g_return_val_if_fail(item != NULL, FALSE);
1782 gtk_ctree_node_set_row_data(ctree, cnode, item);
1783 folderview_update_node(folderview, cnode);
1788 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1791 FolderView *folderview = (FolderView *)data;
1794 if (GTK_CTREE_ROW(node)->children) {
1795 item = gtk_ctree_node_get_row_data(ctree, node);
1796 g_return_if_fail(item != NULL);
1798 if (!item->collapsed)
1799 gtk_ctree_expand(ctree, node);
1801 folderview_update_node(folderview, node);
1805 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1806 GtkCTreeNode *root, GtkCTreeNode **prev)
1809 GtkCTreeNode *node, *parent, *sibling;
1811 node = gtk_ctree_find_by_row_data(ctree, root, item);
1813 g_warning("%s not found.\n", item->path);
1815 parent = GTK_CTREE_ROW(node)->parent;
1816 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1817 sibling = GTK_CTREE_ROW(*prev)->sibling;
1819 sibling = GTK_CTREE_ROW(parent)->children;
1823 tmp = gtk_ctree_node_get_row_data
1825 if (tmp->stype != F_NORMAL)
1826 sibling = GTK_CTREE_ROW(sibling)->sibling;
1830 if (node != sibling)
1831 gtk_ctree_move(ctree, node, parent, sibling);
1838 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1841 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1842 GtkCTreeNode *prev = NULL;
1844 gtk_clist_freeze(GTK_CLIST(ctree));
1845 gtk_sctree_sort_recursive(ctree, root);
1846 if (root && GTK_CTREE_ROW(root)->parent) {
1847 gtk_clist_thaw(GTK_CLIST(ctree));
1850 set_special_folder(ctree, folder->inbox, root, &prev);
1851 set_special_folder(ctree, folder->outbox, root, &prev);
1852 set_special_folder(ctree, folder->draft, root, &prev);
1853 set_special_folder(ctree, folder->queue, root, &prev);
1854 set_special_folder(ctree, folder->trash, root, &prev);
1855 gtk_clist_thaw(GTK_CLIST(ctree));
1858 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1860 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1863 g_return_if_fail(folder != NULL);
1865 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1866 folderview_gnode_func, folderview);
1867 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1869 folderview_sort_folders(folderview, root, folder);
1872 /* callback functions */
1873 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1874 GdkEventButton *event)
1876 GtkCList *clist = GTK_CLIST(folderview->ctree);
1879 FolderViewPopup *fpopup;
1880 GtkItemFactory *fpopup_factory;
1882 FolderItem *special_trash = NULL, *special_queue = NULL;
1886 item = gtk_clist_get_row_data(clist, row);
1888 item = folderview_get_selected_item(folderview);
1890 g_return_if_fail(item != NULL);
1891 g_return_if_fail(item->folder != NULL);
1892 folder = item->folder;
1894 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1896 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1898 fpopup = g_hash_table_lookup(folderview_popups, "common");
1899 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1902 if (fpopup->set_sensitivity != NULL)
1903 fpopup->set_sensitivity(fpopup_factory, item);
1905 if (NULL != (ac = account_find_from_item(item))) {
1906 special_trash = account_get_special_folder(ac, F_TRASH);
1907 special_queue = account_get_special_folder(ac, F_QUEUE);
1910 if ((item == folder->trash || item == special_trash
1911 || folder_has_parent_of_type(item, F_TRASH)) &&
1912 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1913 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1914 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1915 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1916 && !folder_has_parent_of_type(item, F_TRASH)) {
1917 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1918 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1921 if ((item == folder->queue || item == special_queue
1922 || folder_has_parent_of_type(item, F_QUEUE)) &&
1923 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1924 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1925 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1926 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1927 && !folder_has_parent_of_type(item, F_QUEUE)) {
1928 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1929 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1932 #define SET_SENS(name, sens) \
1933 menu_set_sensitive(fpopup_factory, name, sens)
1935 if ( FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_TYPE(item->folder) == F_IMAP )
1936 SET_SENS("/Download messages", !item->no_select);
1937 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1938 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1939 folderview->selected == folderview->opened);
1940 SET_SENS("/Run processing rules", item->prefs->processing &&
1941 item->total_msgs >= 1);
1942 SET_SENS("/Properties...", !item->no_select);
1943 SET_SENS("/Processing...", item->node->parent != NULL && !item->no_select);
1944 if (item == folder->trash || item == special_trash
1945 || folder_has_parent_of_type(item, F_TRASH)) {
1946 GSList *msglist = folder_item_get_msg_list(item);
1947 SET_SENS("/Empty trash...", msglist != NULL);
1948 procmsg_msg_list_free(msglist);
1950 if (item == folder->queue || item == special_queue
1951 || folder_has_parent_of_type(item, F_QUEUE)) {
1952 GSList *msglist = folder_item_get_msg_list(item);
1953 SET_SENS("/Send queue...", msglist != NULL);
1954 procmsg_msg_list_free(msglist);
1958 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1959 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1960 event->button, event->time);
1963 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1964 FolderView *folderview)
1966 GtkCList *clist = GTK_CLIST(ctree);
1967 gint prev_row = -1, row = -1, column = -1;
1969 if (!event) return FALSE;
1971 if (event->button == 1 || event->button == 2) {
1972 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1973 folderview->open_folder = TRUE;
1975 if (event->type == GDK_2BUTTON_PRESS) {
1976 if (clist->selection) {
1979 node = GTK_CTREE_NODE(clist->selection->data);
1981 gtk_ctree_toggle_expansion(
1984 folderview->open_folder = FALSE;
1991 if (event->button == 2 || event->button == 3) {
1993 if (clist->selection) {
1996 node = GTK_CTREE_NODE(clist->selection->data);
1998 prev_row = gtkut_ctree_get_nth_from_node
1999 (GTK_CTREE(ctree), node);
2002 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
2005 if (prev_row != row) {
2006 gtk_clist_unselect_all(clist);
2007 if (event->button == 2)
2008 folderview_select_node
2010 gtk_ctree_node_nth(GTK_CTREE(ctree),
2013 gtk_clist_select_row(clist, row, column);
2017 if (event->button != 3) return FALSE;
2019 folderview_set_sens_and_popup_menu(folderview, row, event);
2023 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2024 FolderView *folderview)
2026 int row = -1, column = -1;
2028 if (!event) return FALSE;
2030 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2033 if (event->button == 1 && folderview->open_folder == FALSE &&
2034 folderview->opened != NULL) {
2035 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2036 folderview->opened);
2037 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2043 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2044 FolderView *folderview)
2046 if (!event) return FALSE;
2048 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2051 switch (event->keyval) {
2057 if (folderview->selected) {
2058 folderview_select_node(folderview,
2059 folderview->selected);
2064 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2065 gtk_ctree_toggle_expansion(
2066 GTK_CTREE(folderview->ctree),
2067 folderview->selected);
2072 if (folderview->selected) {
2073 if (folderview->opened == folderview->selected &&
2074 (!folderview->summaryview->folder_item ||
2075 folderview->summaryview->folder_item->total_msgs == 0))
2076 folderview_select_next_unread(folderview, TRUE);
2078 folderview_select_node(folderview,
2079 folderview->selected);
2089 typedef struct _PostponedSelectData
2094 FolderView *folderview;
2095 } PostponedSelectData;
2097 static gboolean postpone_select(void *data)
2099 PostponedSelectData *psdata = (PostponedSelectData *)data;
2100 debug_print("trying again\n");
2101 psdata->folderview->open_folder = TRUE;
2102 main_window_cursor_normal(psdata->folderview->mainwin);
2103 STATUSBAR_POP(psdata->folderview->mainwin);
2104 folderview_selected(psdata->ctree, psdata->row,
2105 psdata->column, psdata->folderview);
2110 void folderview_close_opened(FolderView *folderview)
2112 if (folderview->opened) {
2113 FolderItem *olditem;
2115 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2116 folderview->opened);
2118 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2119 olditem->path ? olditem->path:olditem->name);
2120 /* will be null if we just moved the previously opened folder */
2121 STATUSBAR_PUSH(folderview->mainwin, buf);
2122 main_window_cursor_wait(folderview->mainwin);
2124 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2125 summary_show(folderview->summaryview, NULL);
2126 folder_item_close(olditem);
2127 main_window_cursor_normal(folderview->mainwin);
2128 STATUSBAR_POP(folderview->mainwin);
2132 if (folderview->opened &&
2133 !GTK_CTREE_ROW(folderview->opened)->children)
2134 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2136 folderview->opened = NULL;
2138 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2139 gint column, FolderView *folderview)
2141 static gboolean can_select = TRUE; /* exclusive lock */
2146 GtkCTreeNode *old_opened = folderview->opened;
2148 folderview->selected = row;
2150 debug_print("newly selected %p, opened %p\n", folderview->selected,
2151 folderview->opened);
2152 if (folderview->opened == row) {
2153 folderview->open_folder = FALSE;
2158 item = gtk_ctree_node_get_row_data(ctree, row);
2161 folderview->open_folder = FALSE;
2165 if (!can_select || summary_is_locked(folderview->summaryview)) {
2166 if (folderview->opened) {
2167 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2168 gtk_ctree_select(ctree, folderview->opened);
2170 folderview->open_folder = FALSE;
2175 if (!folderview->open_folder) {
2182 /* Save cache for old folder */
2183 /* We don't want to lose all caches if sylpheed crashed */
2184 /* resets folderview->opened to NULL */
2185 folderview_close_opened(folderview);
2187 /* CLAWS: set compose button type: news folder items
2188 * always have a news folder as parent */
2190 toolbar_set_compose_button
2191 (folderview->mainwin->toolbar,
2192 FOLDER_TYPE(item->folder) == F_NEWS ?
2193 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2196 debug_print("Folder %s is selected\n", item->path);
2198 if (!GTK_CTREE_ROW(row)->children)
2199 gtk_ctree_expand(ctree, row);
2201 /* ungrab the mouse event */
2202 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2203 gtk_grab_remove(GTK_WIDGET(ctree));
2204 if (gdk_pointer_is_grabbed())
2205 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2209 /* TODO: wwp: avoid displaying (null) in the status bar */
2210 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2211 item->path : "(null)");
2212 debug_print("%s\n", buf);
2213 STATUSBAR_PUSH(folderview->mainwin, buf);
2216 main_window_cursor_wait(folderview->mainwin);
2218 if (folderview->scanning_folder == item->folder) {
2221 res = folder_item_open(item);
2224 if (res == -1 && item->no_select == FALSE) {
2225 main_window_cursor_normal(folderview->mainwin);
2226 STATUSBAR_POP(folderview->mainwin);
2228 alertpanel_error(_("Folder could not be opened."));
2230 folderview->open_folder = FALSE;
2234 } else if (res == -2 && item->no_select == FALSE) {
2235 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2236 data->ctree = ctree;
2238 data->column = column;
2239 data->folderview = folderview;
2240 debug_print("postponing open of %s till end of scan\n",
2241 item->path ? item->path:item->name);
2242 folderview->open_folder = FALSE;
2244 g_timeout_add(500, postpone_select, data);
2249 main_window_cursor_normal(folderview->mainwin);
2252 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2253 opened = summary_show(folderview->summaryview, item);
2255 folder_clean_cache_memory(item);
2258 gtkut_ctree_set_focus_row(ctree, old_opened);
2259 gtk_ctree_select(ctree, old_opened);
2260 folderview->opened = old_opened;
2262 folderview->opened = row;
2263 if (gtk_ctree_node_is_visible(ctree, row)
2264 != GTK_VISIBILITY_FULL)
2265 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2268 STATUSBAR_POP(folderview->mainwin);
2270 folderview->open_folder = FALSE;
2275 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2276 FolderView *folderview)
2280 item = gtk_ctree_node_get_row_data(ctree, node);
2281 g_return_if_fail(item != NULL);
2282 item->collapsed = FALSE;
2283 folderview_update_node(folderview, node);
2286 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2287 FolderView *folderview)
2291 item = gtk_ctree_node_get_row_data(ctree, node);
2292 g_return_if_fail(item != NULL);
2293 item->collapsed = TRUE;
2294 folderview_update_node(folderview, node);
2297 static void folderview_popup_close(GtkMenuShell *menu_shell,
2298 FolderView *folderview)
2300 if (!folderview->opened) return;
2302 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2305 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2306 FolderView *folderview)
2308 FolderColumnType type = folderview->col_state[column].type;
2310 prefs_common.folder_col_size[type] = width;
2313 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2317 folderview_create_folder_node(folderview, item);
2319 if (!item || !item->folder || !item->folder->node)
2322 srcnode = item->folder->node;
2323 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2324 srcnode = srcnode->children;
2325 while (srcnode != NULL) {
2326 if (srcnode && srcnode->data) {
2327 FolderItem *next_item = (FolderItem*) srcnode->data;
2328 folderview_create_folder_node_recursive(folderview, next_item);
2330 srcnode = srcnode->next;
2334 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2336 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2337 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2338 GtkCTreeNode *node, *parent_node;
2339 gint *col_pos = folderview->col_pos;
2340 FolderItemUpdateData hookdata;
2342 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2343 if (parent_node == NULL)
2346 gtk_clist_freeze(GTK_CLIST(ctree));
2348 text[col_pos[F_COL_FOLDER]] = item->name;
2349 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2351 folderxpm, folderxpmmask,
2352 folderopenxpm, folderopenxpmmask,
2354 gtk_ctree_expand(ctree, parent_node);
2355 gtk_ctree_node_set_row_data(ctree, node, item);
2357 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2358 folderview_sort_folders(folderview, parent_node, item->folder);
2360 hookdata.item = item;
2361 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2362 hookdata.msg = NULL;
2363 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2365 gtk_clist_thaw(GTK_CLIST(ctree));
2368 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2371 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2373 GSList *mlist = NULL;
2375 FolderItem *special_trash = NULL;
2378 if (!folderview->selected) return;
2379 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2380 g_return_if_fail(item != NULL);
2381 g_return_if_fail(item->folder != NULL);
2383 if (NULL != (ac = account_find_from_item(item)))
2384 special_trash = account_get_special_folder(ac, F_TRASH);
2386 if (item != item->folder->trash && item != special_trash
2387 && !folder_has_parent_of_type(item, F_TRASH)) return;
2389 if (prefs_common.ask_on_clean) {
2390 if (alertpanel(_("Empty trash"),
2391 _("Delete all messages in trash?"),
2392 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2396 mlist = folder_item_get_msg_list(item);
2398 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2399 MsgInfo * msginfo = (MsgInfo *) cur->data;
2400 if (MSG_IS_LOCKED(msginfo->flags))
2402 /* is it partially received? (partial_recv isn't cached) */
2403 if (msginfo->total_size != 0 &&
2404 msginfo->size != (off_t)msginfo->total_size)
2405 partial_mark_for_delete(msginfo);
2407 procmsg_msg_list_free(mlist);
2409 folder_item_remove_all_msg(item);
2412 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2415 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2417 FolderItem *special_queue = NULL;
2419 gchar *errstr = NULL;
2421 if (!folderview->selected) return;
2422 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2423 g_return_if_fail(item != NULL);
2424 g_return_if_fail(item->folder != NULL);
2426 if (NULL != (ac = account_find_from_item(item)))
2427 special_queue = account_get_special_folder(ac, F_QUEUE);
2429 if (item != item->folder->queue && item != special_queue
2430 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2432 if (procmsg_queue_is_empty(item))
2435 if (prefs_common.work_offline)
2436 if (alertpanel(_("Offline warning"),
2437 _("You're working offline. Override?"),
2438 GTK_STOCK_NO, GTK_STOCK_YES,
2439 NULL) != G_ALERTALTERNATE)
2442 /* ask for confirmation before sending queued messages only
2443 in online mode and if there is at least one message queued
2444 in any of the folder queue
2446 if (prefs_common.confirm_send_queued_messages) {
2447 if (!prefs_common.work_offline) {
2448 if (alertpanel(_("Send queued messages"),
2449 _("Send all queued messages?"),
2450 GTK_STOCK_CANCEL, _("_Send"),
2451 NULL) != G_ALERTALTERNATE)
2456 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2458 alertpanel_error_log(_("Some errors occurred while "
2459 "sending queued messages."));
2461 alertpanel_error_log(_("Some errors occurred "
2462 "while sending queued messages:\n%s"), errstr);
2468 static void folderview_search_cb(FolderView *folderview, guint action,
2471 summary_search(folderview->summaryview);
2474 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2477 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2480 if (!folderview->selected) return;
2482 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2483 g_return_if_fail(item != NULL);
2484 g_return_if_fail(item->folder != NULL);
2486 folder_item_apply_processing(item);
2489 static void folderview_property_cb(FolderView *folderview, guint action,
2492 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2495 if (!folderview->selected) return;
2497 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2498 g_return_if_fail(item != NULL);
2499 g_return_if_fail(item->folder != NULL);
2501 prefs_folder_item_open(item);
2504 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2506 GSList *list = NULL;
2507 GSList *done = NULL;
2508 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2510 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2511 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2512 && list->data != node) {
2513 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2514 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2517 for (list = done; list != NULL; list = g_slist_next(list)) {
2518 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2524 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2525 FolderItem *to_folder, gboolean copy)
2527 FolderItem *from_parent = NULL;
2528 FolderItem *new_folder = NULL;
2529 GtkCTreeNode *src_node = NULL;
2533 g_return_if_fail(folderview != NULL);
2534 g_return_if_fail(from_folder != NULL);
2535 g_return_if_fail(to_folder != NULL);
2537 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2538 from_parent = folder_item_parent(from_folder);
2540 if (prefs_common.warn_dnd) {
2541 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2542 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2543 from_folder->name, to_folder->name);
2544 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2545 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2546 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2549 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2551 else if (status & G_ALERTDISABLE)
2552 prefs_common.warn_dnd = FALSE;
2555 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2556 from_folder->name, to_folder->name);
2557 STATUSBAR_PUSH(folderview->mainwin, buf);
2559 summary_clear_all(folderview->summaryview);
2560 folderview->opened = NULL;
2561 folderview->selected = NULL;
2562 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2564 main_window_cursor_wait(folderview->mainwin);
2566 statusbar_verbosity_set(FALSE);
2567 folder_item_update_freeze();
2568 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2569 statusbar_verbosity_set(FALSE);
2570 main_window_cursor_normal(folderview->mainwin);
2571 STATUSBAR_POP(folderview->mainwin);
2572 folder_item_update_thaw();
2573 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2575 folderview_sort_folders(folderview,
2576 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2577 NULL, to_folder), new_folder->folder);
2578 folderview_select(folderview, new_folder);
2580 statusbar_verbosity_set(FALSE);
2581 main_window_cursor_normal(folderview->mainwin);
2582 STATUSBAR_POP(folderview->mainwin);
2583 folder_item_update_thaw();
2585 case F_MOVE_FAILED_DEST_IS_PARENT:
2586 alertpanel_error(_("Source and destination are the same."));
2588 case F_MOVE_FAILED_DEST_IS_CHILD:
2589 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2590 _("Can't move a folder to one of its children."));
2592 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2593 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2596 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2601 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2604 static gint folderview_clist_compare(GtkCList *clist,
2605 gconstpointer ptr1, gconstpointer ptr2)
2607 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2608 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2610 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2612 return item1->order - item2->order;
2615 // if only one folder has an order it comes first
2616 if (item1->order > 0)
2620 if (item2->order > 0)
2626 return (item2->name != NULL);
2630 return g_utf8_collate(item1->name, item2->name);
2633 static void folderview_processing_cb(FolderView *folderview, guint action,
2636 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2640 if (!folderview->selected) return;
2642 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2643 g_return_if_fail(item != NULL);
2644 g_return_if_fail(item->folder != NULL);
2646 id = folder_item_get_identifier(item);
2647 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2650 prefs_filtering_open(&item->prefs->processing, title,
2651 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2655 void folderview_set_target_folder_color(gint color_op)
2659 FolderView *folderview;
2661 for (list = folderview_list; list != NULL; list = list->next) {
2662 folderview = (FolderView *)list->data;
2663 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2665 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2666 folderview->color_op;
2672 static gchar *last_font = NULL;
2673 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2681 void folderview_reflect_prefs(void)
2683 gboolean update_font = TRUE;
2684 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2685 FolderItem *item = folderview_get_selected_item(folderview);
2686 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2687 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2688 gint height = pos->value;
2690 if (last_font && !strcmp(last_font, NORMAL_FONT))
2691 update_font = FALSE;
2694 last_font = g_strdup(NORMAL_FONT);
2697 normal_style = normal_color_style = bold_style =
2698 bold_color_style = bold_tgtfold_style = NULL;
2700 folderview_init(folderview);
2702 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2703 folderview_column_set_titles(folderview);
2704 folderview_set_all();
2706 g_signal_handlers_block_by_func
2707 (G_OBJECT(folderview->ctree),
2708 G_CALLBACK(folderview_selected), folderview);
2711 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2712 GTK_CTREE(folderview->ctree), NULL, item);
2714 folderview_select(folderview, item);
2715 folderview->open_folder = FALSE;
2716 folderview->selected = node;
2719 g_signal_handlers_unblock_by_func
2720 (G_OBJECT(folderview->ctree),
2721 G_CALLBACK(folderview_selected), folderview);
2723 pos = gtk_scrolled_window_get_vadjustment(
2724 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2725 gtk_adjustment_set_value(pos, height);
2726 gtk_adjustment_changed(pos);
2727 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2730 static void drag_state_stop(FolderView *folderview)
2732 if (folderview->drag_timer)
2733 g_source_remove(folderview->drag_timer);
2734 folderview->drag_timer = 0;
2735 folderview->drag_node = NULL;
2738 static gint folderview_defer_expand(FolderView *folderview)
2740 if (folderview->drag_node) {
2741 folderview_recollapse_nodes(folderview, folderview->drag_node);
2742 if (folderview->drag_item->collapsed) {
2743 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2744 folderview->nodes_to_recollapse = g_slist_append
2745 (folderview->nodes_to_recollapse, folderview->drag_node);
2748 folderview->drag_item = NULL;
2749 folderview->drag_timer = 0;
2753 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2755 /* the idea is that we call drag_state_start() whenever we want expansion to
2756 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2757 * we need to call drag_state_stop() */
2758 drag_state_stop(folderview);
2759 /* request expansion */
2760 if (0 != (folderview->drag_timer = g_timeout_add
2761 (prefs_common.hover_timeout,
2762 (GtkFunction)folderview_defer_expand,
2764 folderview->drag_node = node;
2765 folderview->drag_item = item;
2769 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2770 FolderView *folderview)
2772 GdkDragContext *context;
2774 g_return_if_fail(folderview != NULL);
2775 if (folderview->selected == NULL) return;
2776 if (folderview->nodes_to_recollapse)
2777 g_slist_free(folderview->nodes_to_recollapse);
2778 folderview->nodes_to_recollapse = NULL;
2779 context = gtk_drag_begin(widget, folderview->target_list,
2780 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2781 gtk_drag_set_icon_default(context);
2784 static void folderview_drag_data_get(GtkWidget *widget,
2785 GdkDragContext *drag_context,
2786 GtkSelectionData *selection_data,
2789 FolderView *folderview)
2793 gchar *source = NULL;
2794 if (info == TARGET_DUMMY) {
2795 for (cur = GTK_CLIST(folderview->ctree)->selection;
2796 cur != NULL; cur = cur->next) {
2797 item = gtk_ctree_node_get_row_data
2798 (GTK_CTREE(folderview->ctree),
2799 GTK_CTREE_NODE(cur->data));
2801 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2802 gtk_selection_data_set(selection_data,
2803 selection_data->target, 8,
2804 source, strlen(source));
2810 g_warning("unknown info %d\n", info);
2814 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2816 FolderUpdateData *hookdata;
2817 FolderView *folderview;
2821 folderview = (FolderView *) userdata;
2822 g_return_val_if_fail(hookdata != NULL, FALSE);
2823 g_return_val_if_fail(folderview != NULL, FALSE);
2825 ctree = folderview->ctree;
2826 g_return_val_if_fail(ctree != NULL, FALSE);
2828 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2829 folderview_create_folder_node(folderview, hookdata->item);
2830 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2831 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2832 NULL, folder_item_parent(hookdata->item));
2833 folderview_sort_folders(folderview, node, hookdata->folder);
2834 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2837 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2839 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2840 if (folderview->selected == node)
2841 folderview->selected = NULL;
2842 if (folderview->opened == node)
2843 folderview->opened = NULL;
2845 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2846 folderview_set(folderview);
2851 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2852 GdkDragContext *context,
2856 FolderView *folderview)
2859 FolderItem *item = NULL, *src_item = NULL;
2860 GtkCTreeNode *node = NULL;
2861 gboolean acceptable = FALSE;
2862 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2863 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2864 int height = (int)pos->page_size;
2865 int total_height = (int)pos->upper;
2866 int vpos = (int) pos->value;
2867 int offset = prefs_common.show_col_headers ? 24:0;
2869 if (gtk_clist_get_selection_info
2870 (GTK_CLIST(widget), x - offset, y - offset, &row, &column)) {
2871 GtkWidget *srcwidget;
2873 if (y > height - 24 && height + vpos < total_height) {
2874 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2875 gtk_adjustment_changed(pos);
2877 if (y < 48 && y > 0) {
2878 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2879 gtk_adjustment_changed(pos);
2882 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2883 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2884 src_item = folderview->summaryview->folder_item;
2886 srcwidget = gtk_drag_get_source_widget(context);
2887 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2888 /* comes from summaryview */
2889 /* we are copying messages, so only accept folder items that are not
2890 the source item, are no root items and can copy messages */
2891 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2892 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2893 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2895 } else if (srcwidget == folderview->ctree) {
2896 /* comes from folderview */
2897 /* we are moving folder items, only accept folders that are not
2898 the source items and can copy messages and create folder items */
2899 if (item && item->folder && src_item && src_item != item &&
2900 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2901 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2902 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2903 || item->folder == src_item->folder))
2906 /* comes from another app */
2907 /* we are adding messages, so only accept folder items that are
2908 no root items and can copy messages */
2909 if (item && item->folder && folder_item_parent(item) != NULL
2910 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2911 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2916 if (acceptable || (src_item && src_item == item))
2917 drag_state_start(folderview, node, item);
2920 g_signal_handlers_block_by_func
2922 G_CALLBACK(folderview_selected), folderview);
2923 gtk_ctree_select(GTK_CTREE(widget), node);
2924 g_signal_handlers_unblock_by_func
2926 G_CALLBACK(folderview_selected), folderview);
2927 gdk_drag_status(context,
2928 (context->actions == GDK_ACTION_COPY ?
2929 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2931 if (folderview->opened)
2932 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2933 gdk_drag_status(context, 0, time);
2939 static void folderview_drag_leave_cb(GtkWidget *widget,
2940 GdkDragContext *context,
2942 FolderView *folderview)
2944 drag_state_stop(folderview);
2945 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2948 static void free_info (gpointer stuff, gpointer data)
2953 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2954 guint time, FolderItem *item)
2957 GSList *msglist = NULL;
2958 list = uri_list_extract_filenames(data);
2959 if (!(item && item->folder && folder_item_parent(item) != NULL
2960 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2962 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2963 debug_print("item doesn't fit\n");
2967 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2968 debug_print("list is empty\n");
2971 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2972 MsgFileInfo *info = NULL;
2974 if (file_is_email((gchar *)tmp->data)) {
2975 info = g_new0(MsgFileInfo, 1);
2976 info->msginfo = NULL;
2977 info->file = (gchar *)tmp->data;
2978 msglist = g_slist_prepend(msglist, info);
2979 debug_print("file is a mail\n");
2981 debug_print("file isn't a mail\n");
2985 msglist = g_slist_reverse(msglist);
2986 folder_item_add_msgs(item, msglist, FALSE);
2987 g_slist_foreach(msglist, free_info, NULL);
2988 g_slist_free(msglist);
2989 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2991 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2993 list_free_strings(list);
2997 static void folderview_drag_received_cb(GtkWidget *widget,
2998 GdkDragContext *drag_context,
3001 GtkSelectionData *data,
3004 FolderView *folderview)
3007 FolderItem *item = NULL, *src_item;
3009 int offset = prefs_common.show_col_headers ? 24:0;
3011 if (info == TARGET_DUMMY) {
3012 drag_state_stop(folderview);
3013 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
3014 /* comes from summaryview */
3015 if (gtk_clist_get_selection_info
3016 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3019 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3020 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3021 src_item = folderview->summaryview->folder_item;
3023 if (item->no_select) {
3024 alertpanel_error(_("The destination folder can only be used to "
3025 "store subfolders."));
3028 /* re-check (due to acceptable possibly set for folder moves */
3029 if (!(item && item->folder && item->path && !item->no_select &&
3030 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3033 if (item && src_item) {
3034 switch (drag_context->action) {
3035 case GDK_ACTION_COPY:
3036 summary_copy_selected_to(folderview->summaryview, item);
3037 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3039 case GDK_ACTION_MOVE:
3040 case GDK_ACTION_DEFAULT:
3042 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3043 summary_copy_selected_to(folderview->summaryview, item);
3045 summary_move_selected_to(folderview->summaryview, item);
3046 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3049 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3051 /* comes from folderview */
3053 gboolean folder_is_normal = TRUE;
3054 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3056 source = data->data + 17;
3057 if (gtk_clist_get_selection_info
3058 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0
3060 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3063 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3064 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3065 src_item = folder_find_item_from_identifier(source);
3069 src_item->stype == F_NORMAL &&
3070 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3071 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3072 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3073 !folder_has_parent_of_type(src_item, F_TRASH);
3074 if (!item || !src_item || !folder_is_normal) {
3075 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3079 folderview_move_folder(folderview, src_item, item, copy);
3080 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3082 folderview->nodes_to_recollapse = NULL;
3083 } else if (info == TARGET_MAIL_URI_LIST) {
3084 if (gtk_clist_get_selection_info
3085 (GTK_CLIST(widget), x - offset, y - offset, &row, &column) == 0)
3088 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3090 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3091 debug_print("no node\n");
3094 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3096 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3097 debug_print("no item\n");
3100 folderview_finish_dnd(data->data, drag_context, time, item);
3104 static void folderview_drag_end_cb(GtkWidget *widget,
3105 GdkDragContext *drag_context,
3106 FolderView *folderview)
3108 drag_state_stop(folderview);
3109 g_slist_free(folderview->nodes_to_recollapse);
3110 folderview->nodes_to_recollapse = NULL;
3113 void folderview_register_popup(FolderViewPopup *fpopup)
3117 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3118 FolderView *folderview = folderviews->data;
3119 GtkItemFactory *factory;
3121 factory = create_ifactory(folderview, fpopup);
3122 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3124 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3127 void folderview_unregister_popup(FolderViewPopup *fpopup)
3131 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3132 FolderView *folderview = folderviews->data;
3134 g_hash_table_remove(folderview->popups, fpopup->klass);
3136 g_hash_table_remove(folderview_popups, fpopup->klass);