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 g_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 g_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);
1134 label_window_destroy(window);
1138 void folderview_fast_rescan_tree(Folder *folder)
1141 MainWindow *mainwin = mainwindow_get_mainwindow();
1142 FolderView *folderview = NULL;
1143 GtkAdjustment *pos = NULL;
1146 g_return_if_fail(folder != NULL);
1148 if (!folder->klass->scan_tree) return;
1152 window = label_window_create(_("Scanning folder tree..."));
1155 folderview = mainwin->folderview;
1158 pos = gtk_scrolled_window_get_vadjustment(
1159 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1160 height = pos->value;
1163 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1164 folder_fast_scan_tree(folder);
1165 folder_set_ui_func(folder, NULL, NULL);
1167 folderview_set_all();
1170 pos = gtk_scrolled_window_get_vadjustment(
1171 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1172 gtk_adjustment_set_value(pos, height);
1174 label_window_destroy(window);
1178 /** folderview_check_new()
1179 * Scan and update the folder and return the
1180 * count the number of new messages since last check.
1181 * \param folder the folder to check for new messages
1182 * \return the number of new messages since last check
1184 gint folderview_check_new(Folder *folder)
1188 FolderView *folderview;
1192 gint former_new_msgs = 0;
1193 gint former_new = 0, former_unread = 0, former_total;
1195 for (list = folderview_list; list != NULL; list = list->next) {
1196 folderview = (FolderView *)list->data;
1197 ctree = GTK_CTREE(folderview->ctree);
1200 main_window_lock(folderview->mainwin);
1202 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1203 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1205 item = gtk_ctree_node_get_row_data(ctree, node);
1206 if (!item || !item->path || !item->folder) continue;
1207 if (item->no_select) continue;
1208 if (folder && folder != item->folder) continue;
1209 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1210 if (!item->prefs->newmailcheck) continue;
1211 if (item->processing_pending == TRUE) {
1212 debug_print("skipping %s, processing pending\n",
1213 item->path ? item->path : item->name);
1217 str = get_scan_str(item);
1219 STATUSBAR_PUSH(folderview->mainwin, str);
1223 folderview_scan_tree_func(item->folder, item, NULL);
1224 former_new = item->new_msgs;
1225 former_unread = item->unread_msgs;
1226 former_total = item->total_msgs;
1228 if (item->folder->klass->scan_required &&
1229 (item->folder->klass->scan_required(item->folder, item) ||
1230 item->folder->inbox == item ||
1231 item->opened == TRUE ||
1232 item->processing_pending == TRUE)) {
1233 if (folder_item_scan(item) < 0) {
1235 summaryview_unlock(folderview->summaryview, item);
1236 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1237 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1238 item->path ? item->path:item->name);
1239 STATUSBAR_POP(folderview->mainwin);
1241 } else if (!FOLDER_IS_LOCAL(folder)) {
1242 STATUSBAR_POP(folderview->mainwin);
1247 } else if (!item->folder->klass->scan_required) {
1248 if (folder_item_scan(item) < 0) {
1249 summaryview_unlock(folderview->summaryview, item);
1250 if (folder && !FOLDER_IS_LOCAL(folder)) {
1251 STATUSBAR_POP(folderview->mainwin);
1256 if (former_new != item->new_msgs ||
1257 former_unread != item->unread_msgs ||
1258 former_total != item->total_msgs)
1259 folderview_update_node(folderview, node);
1261 new_msgs += item->new_msgs;
1262 former_new_msgs += former_new;
1263 STATUSBAR_POP(folderview->mainwin);
1266 main_window_unlock(folderview->mainwin);
1270 folder_write_list();
1271 /* Number of new messages since last check is the just the difference
1272 * between former_new_msgs and new_msgs. If new_msgs is less than
1273 * former_new_msgs, that would mean another session accessed the folder
1274 * and the result is not well defined.
1276 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1280 void folderview_check_new_all(void)
1284 FolderView *folderview;
1286 folderview = (FolderView *)folderview_list->data;
1289 main_window_lock(folderview->mainwin);
1290 window = label_window_create
1291 (_("Checking for new messages in all folders..."));
1293 list = folder_get_list();
1294 for (; list != NULL; list = list->next) {
1295 Folder *folder = list->data;
1297 folderview_check_new(folder);
1300 folder_write_list();
1301 folderview_set_all();
1303 label_window_destroy(window);
1304 main_window_unlock(folderview->mainwin);
1308 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1314 if (!item || !item->folder || !item->folder->node)
1317 node = item->folder->node;
1319 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1320 node = node->children;
1323 (item->new_msgs > 0 ||
1324 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1328 while (node != NULL) {
1329 if (node && node->data) {
1330 FolderItem *next_item = (FolderItem*) node->data;
1332 if (folderview_have_new_children_sub(folderview,
1341 static gboolean folderview_have_new_children(FolderView *folderview,
1344 return folderview_have_new_children_sub(folderview, item, FALSE);
1347 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1353 if (!item || !item->folder || !item->folder->node)
1356 node = item->folder->node;
1358 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1359 node = node->children;
1362 (item->unread_msgs > 0 ||
1363 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1367 while (node != NULL) {
1368 if (node && node->data) {
1369 FolderItem *next_item = (FolderItem*) node->data;
1371 if (folderview_have_unread_children_sub(folderview,
1381 static gboolean folderview_have_unread_children(FolderView *folderview,
1384 return folderview_have_unread_children_sub(folderview, item, FALSE);
1387 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1393 if (!item || !item->folder || !item->folder->node)
1396 node = item->folder->node;
1398 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1399 node = node->children;
1401 if (in_sub && item->search_match){
1405 while (node != NULL) {
1406 if (node && node->data) {
1407 FolderItem *next_item = (FolderItem*) node->data;
1409 if (folderview_have_matching_children_sub(folderview,
1419 static gboolean folderview_have_matching_children(FolderView *folderview,
1422 return folderview_have_matching_children_sub(folderview, item, FALSE);
1425 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1431 if (!item || !item->folder || !item->folder->node)
1434 node = item->folder->node;
1436 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1437 node = node->children;
1439 if (item->marked_msgs != 0) {
1443 while (node != NULL) {
1444 if (node && node->data) {
1445 FolderItem *next_item = (FolderItem*) node->data;
1447 if (folderview_have_marked_children_sub(folderview,
1456 static gboolean folderview_have_marked_children(FolderView *folderview,
1459 return folderview_have_marked_children_sub(folderview, item, FALSE);
1462 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1464 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1465 GtkStyle *style = NULL;
1466 GtkStyle *color_style = NULL;
1468 GdkPixmap *xpm, *openxpm;
1469 GdkBitmap *mask, *openmask;
1470 static GdkPixmap *searchicon;
1471 static GdkBitmap *searchmask;
1472 gboolean mark = FALSE;
1475 gboolean add_unread_mark;
1476 gboolean add_sub_match_mark;
1477 gboolean use_bold, use_color;
1478 gint *col_pos = folderview->col_pos;
1479 SpecialFolderItemType stype;
1481 item = gtk_ctree_node_get_row_data(ctree, node);
1482 g_return_if_fail(item != NULL);
1484 if (!GTK_CTREE_ROW(node)->expanded)
1485 mark = folderview_have_marked_children(folderview, item);
1487 mark = (item->marked_msgs != 0);
1489 stype = item->stype;
1490 if (stype == F_NORMAL) {
1491 if (folder_has_parent_of_type(item, F_TRASH))
1493 else if (folder_has_parent_of_type(item, F_DRAFT))
1495 else if (folder_has_parent_of_type(item, F_OUTBOX))
1497 else if (folder_has_parent_of_type(item, F_QUEUE))
1502 if (item->hide_read_msgs) {
1503 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1504 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1505 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1506 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1508 xpm = mark?m_inboxxpm:inboxxpm;
1509 mask = mark?m_inboxxpmmask:inboxxpmmask;
1510 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1511 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1515 if (item->hide_read_msgs) {
1516 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1517 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1518 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1519 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1521 xpm = mark?m_outboxxpm:outboxxpm;
1522 mask = mark?m_outboxxpmmask:outboxxpmmask;
1523 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1524 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1528 if (item->hide_read_msgs) {
1529 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1530 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1531 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1532 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1534 xpm = mark?m_queuexpm:queuexpm;
1535 mask = mark?m_queuexpmmask:queuexpmmask;
1536 openxpm = mark?m_queueopenxpm:queueopenxpm;
1537 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1541 if (item->hide_read_msgs) {
1542 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1543 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1544 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1545 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1547 xpm = mark?m_trashxpm:trashxpm;
1548 mask = mark?m_trashxpmmask:trashxpmmask;
1549 openxpm = mark?m_trashopenxpm:trashopenxpm;
1550 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1554 xpm = mark?m_draftsxpm:draftsxpm;
1555 mask = mark?m_draftsxpmmask:draftsxpmmask;
1556 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1557 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1560 if (item->hide_read_msgs) {
1561 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1562 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1563 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1564 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1566 xpm = mark?m_folderxpm:folderxpm;
1567 mask = mark?m_folderxpmmask:folderxpmmask;
1568 openxpm = mark?m_folderopenxpm:folderopenxpm;
1569 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1573 if (item->no_select) {
1574 xpm = openxpm = noselectxpm;
1575 mask = openmask = noselectxpmmask;
1578 name = folder_item_get_name(item);
1580 if (!GTK_CTREE_ROW(node)->expanded) {
1581 add_unread_mark = folderview_have_unread_children(
1583 add_sub_match_mark = folderview_have_matching_children(
1586 add_unread_mark = FALSE;
1587 add_sub_match_mark = FALSE;
1590 if (item->search_match) {
1592 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1593 &searchicon, &searchmask);
1595 xpm = openxpm = searchicon;
1596 mask = openmask = searchmask;
1600 if (prefs_common.display_folder_unread) {
1601 if (folder_has_parent_of_type(item, F_QUEUE)) {
1602 /* only total_msgs matters here */
1603 if (item->total_msgs > 0) {
1604 /* show total number (should be equal to the unread number)
1606 str = g_strdup_printf("%s (%d%s%s)",
1607 name, item->total_msgs,
1608 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1609 (item->unreadmarked_msgs > 0) ? "!" : "");
1612 if (prefs_common.display_folder_unread == 1) {
1613 if (item->unread_msgs > 0) {
1614 /* show unread number and signs */
1615 str = g_strdup_printf("%s (%d%s%s)",
1616 name, item->unread_msgs,
1617 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1618 (item->unreadmarked_msgs > 0) ? "!" : "");
1621 if (item->total_msgs > 0) {
1622 /* show unread number, total number and signs if any */
1623 str = g_strdup_printf("%s (%d/%d%s%s)",
1624 name, item->unread_msgs, item->total_msgs,
1625 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1626 (item->unreadmarked_msgs > 0) ? "!" : "");
1630 if ((str == NULL) &&
1631 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1632 /* no unread/total numbers, but at least one sign */
1633 str = g_strdup_printf("%s (%s%s)",
1635 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1636 (item->unreadmarked_msgs > 0) ? "!" : "");
1640 /* last fallback, folder name only or with ! sign */
1641 str = g_strdup_printf("%s%s",
1642 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1644 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1645 xpm, mask, openxpm, openmask,
1646 FALSE, GTK_CTREE_ROW(node)->expanded);
1650 if (!folder_item_parent(item)) {
1651 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1652 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1653 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1655 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1656 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1657 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1660 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1661 folder_has_parent_of_type(item, F_DRAFT) ||
1662 folder_has_parent_of_type(item, F_TRASH)) {
1663 use_bold = use_color = FALSE;
1664 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1665 /* highlight queue folder if there are any messages */
1666 use_bold = use_color = (item->total_msgs > 0);
1668 /* if unread messages exist, print with bold font */
1669 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1671 /* if new messages exist, print with colored letter */
1673 (item->new_msgs > 0) ||
1675 folderview_have_new_children(folderview, item));
1678 gtk_ctree_node_set_foreground(ctree, node, NULL);
1683 if (item->prefs->color > 0 && !use_color) {
1684 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1685 color_style = gtk_style_copy(bold_style);
1686 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1687 style = color_style;
1688 } else if (use_color) {
1689 style = bold_color_style;
1692 if (item->op_count > 0) {
1693 style = bold_tgtfold_style;
1695 } else if (use_color) {
1696 style = normal_color_style;
1697 gtk_ctree_node_set_foreground(ctree, node,
1698 &folderview->color_new);
1699 } else if (item->op_count > 0) {
1700 style = bold_tgtfold_style;
1701 } else if (item->prefs->color > 0) {
1703 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1704 color_style = gtk_style_copy(normal_style);
1705 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1706 style = color_style;
1708 style = normal_style;
1711 gtk_ctree_node_set_row_style(ctree, node, style);
1713 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1714 folderview_update_node(folderview, node);
1717 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1720 FolderView *folderview;
1724 g_return_if_fail(item != NULL);
1726 for (list = folderview_list; list != NULL; list = list->next) {
1727 folderview = (FolderView *)list->data;
1728 ctree = GTK_CTREE(folderview->ctree);
1730 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1732 item->search_match = matches;
1733 folderview_update_node(folderview, node);
1738 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1740 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1741 FolderView *folderview = (FolderView *)data;
1744 g_return_val_if_fail(update_info != NULL, TRUE);
1745 g_return_val_if_fail(update_info->item != NULL, TRUE);
1746 g_return_val_if_fail(folderview != NULL, FALSE);
1748 ctree = GTK_CTREE(folderview->ctree);
1750 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1753 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1754 folderview_update_node(folderview, node);
1756 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1757 update_info->item == folderview->summaryview->folder_item &&
1758 update_info->item != NULL)
1759 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1760 summary_show(folderview->summaryview, update_info->item);
1766 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1767 GNode *gnode, GtkCTreeNode *cnode,
1770 FolderView *folderview = (FolderView *)data;
1771 FolderItem *item = FOLDER_ITEM(gnode->data);
1773 g_return_val_if_fail(item != NULL, FALSE);
1775 gtk_ctree_node_set_row_data(ctree, cnode, item);
1776 folderview_update_node(folderview, cnode);
1781 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1784 FolderView *folderview = (FolderView *)data;
1787 if (GTK_CTREE_ROW(node)->children) {
1788 item = gtk_ctree_node_get_row_data(ctree, node);
1789 g_return_if_fail(item != NULL);
1791 if (!item->collapsed)
1792 gtk_ctree_expand(ctree, node);
1794 folderview_update_node(folderview, node);
1798 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1799 GtkCTreeNode *root, GtkCTreeNode **prev)
1802 GtkCTreeNode *node, *parent, *sibling;
1804 node = gtk_ctree_find_by_row_data(ctree, root, item);
1806 g_warning("%s not found.\n", item->path);
1808 parent = GTK_CTREE_ROW(node)->parent;
1809 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1810 sibling = GTK_CTREE_ROW(*prev)->sibling;
1812 sibling = GTK_CTREE_ROW(parent)->children;
1816 tmp = gtk_ctree_node_get_row_data
1818 if (tmp->stype != F_NORMAL)
1819 sibling = GTK_CTREE_ROW(sibling)->sibling;
1823 if (node != sibling)
1824 gtk_ctree_move(ctree, node, parent, sibling);
1831 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1834 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1835 GtkCTreeNode *prev = NULL;
1837 gtk_clist_freeze(GTK_CLIST(ctree));
1838 gtk_sctree_sort_recursive(ctree, root);
1839 if (root && GTK_CTREE_ROW(root)->parent) {
1840 gtk_clist_thaw(GTK_CLIST(ctree));
1843 set_special_folder(ctree, folder->inbox, root, &prev);
1844 set_special_folder(ctree, folder->outbox, root, &prev);
1845 set_special_folder(ctree, folder->draft, root, &prev);
1846 set_special_folder(ctree, folder->queue, root, &prev);
1847 set_special_folder(ctree, folder->trash, root, &prev);
1848 gtk_clist_thaw(GTK_CLIST(ctree));
1851 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1853 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1856 g_return_if_fail(folder != NULL);
1858 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1859 folderview_gnode_func, folderview);
1860 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1862 folderview_sort_folders(folderview, root, folder);
1865 /* callback functions */
1866 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1867 GdkEventButton *event)
1869 GtkCList *clist = GTK_CLIST(folderview->ctree);
1872 FolderViewPopup *fpopup;
1873 GtkItemFactory *fpopup_factory;
1875 FolderItem *special_trash = NULL, *special_queue = NULL;
1879 item = gtk_clist_get_row_data(clist, row);
1881 item = folderview_get_selected_item(folderview);
1883 g_return_if_fail(item != NULL);
1884 g_return_if_fail(item->folder != NULL);
1885 folder = item->folder;
1887 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1889 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1891 fpopup = g_hash_table_lookup(folderview_popups, "common");
1892 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1895 if (fpopup->set_sensitivity != NULL)
1896 fpopup->set_sensitivity(fpopup_factory, item);
1898 if (NULL != (ac = account_find_from_item(item))) {
1899 special_trash = account_get_special_folder(ac, F_TRASH);
1900 special_queue = account_get_special_folder(ac, F_QUEUE);
1903 if ((item == folder->trash || item == special_trash
1904 || folder_has_parent_of_type(item, F_TRASH)) &&
1905 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1906 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1907 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1908 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1909 && !folder_has_parent_of_type(item, F_TRASH)) {
1910 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1911 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1914 if ((item == folder->queue || item == special_queue
1915 || folder_has_parent_of_type(item, F_QUEUE)) &&
1916 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1917 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1918 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1919 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1920 && !folder_has_parent_of_type(item, F_QUEUE)) {
1921 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1922 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1925 #define SET_SENS(name, sens) \
1926 menu_set_sensitive(fpopup_factory, name, sens)
1928 if ( FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_TYPE(item->folder) == F_IMAP )
1929 SET_SENS("/Download messages", !item->no_select);
1930 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1931 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1932 folderview->selected == folderview->opened);
1933 SET_SENS("/Run processing rules", item->prefs->processing &&
1934 item->total_msgs >= 1);
1935 SET_SENS("/Properties...", !item->no_select);
1936 SET_SENS("/Processing...", item->node->parent != NULL && !item->no_select);
1937 if (item == folder->trash || item == special_trash
1938 || folder_has_parent_of_type(item, F_TRASH)) {
1939 GSList *msglist = folder_item_get_msg_list(item);
1940 SET_SENS("/Empty trash...", msglist != NULL);
1941 procmsg_msg_list_free(msglist);
1943 if (item == folder->queue || item == special_queue
1944 || folder_has_parent_of_type(item, F_QUEUE)) {
1945 GSList *msglist = folder_item_get_msg_list(item);
1946 SET_SENS("/Send queue...", msglist != NULL);
1947 procmsg_msg_list_free(msglist);
1951 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1952 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1953 event->button, event->time);
1956 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1957 FolderView *folderview)
1959 GtkCList *clist = GTK_CLIST(ctree);
1960 gint prev_row = -1, row = -1, column = -1;
1962 if (!event) return FALSE;
1964 if (event->button == 1 || event->button == 2) {
1965 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1966 folderview->open_folder = TRUE;
1968 if (event->type == GDK_2BUTTON_PRESS) {
1969 if (clist->selection) {
1972 node = GTK_CTREE_NODE(clist->selection->data);
1974 gtk_ctree_toggle_expansion(
1977 folderview->open_folder = FALSE;
1984 if (event->button == 2 || event->button == 3) {
1986 if (clist->selection) {
1989 node = GTK_CTREE_NODE(clist->selection->data);
1991 prev_row = gtkut_ctree_get_nth_from_node
1992 (GTK_CTREE(ctree), node);
1995 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1998 if (prev_row != row) {
1999 gtk_clist_unselect_all(clist);
2000 if (event->button == 2)
2001 folderview_select_node
2003 gtk_ctree_node_nth(GTK_CTREE(ctree),
2006 gtk_clist_select_row(clist, row, column);
2010 if (event->button != 3) return FALSE;
2012 folderview_set_sens_and_popup_menu(folderview, row, event);
2016 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2017 FolderView *folderview)
2019 int row = -1, column = -1;
2021 if (!event) return FALSE;
2023 if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), event->x, event->y,
2026 if (event->button == 1 && folderview->open_folder == FALSE &&
2027 folderview->opened != NULL) {
2028 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2029 folderview->opened);
2030 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2036 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2037 FolderView *folderview)
2039 if (!event) return FALSE;
2041 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2044 switch (event->keyval) {
2050 if (folderview->selected) {
2051 folderview_select_node(folderview,
2052 folderview->selected);
2057 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2058 gtk_ctree_toggle_expansion(
2059 GTK_CTREE(folderview->ctree),
2060 folderview->selected);
2065 if (folderview->selected) {
2066 if (folderview->opened == folderview->selected &&
2067 (!folderview->summaryview->folder_item ||
2068 folderview->summaryview->folder_item->total_msgs == 0))
2069 folderview_select_next_unread(folderview, TRUE);
2071 folderview_select_node(folderview,
2072 folderview->selected);
2082 typedef struct _PostponedSelectData
2087 FolderView *folderview;
2088 } PostponedSelectData;
2090 static gboolean postpone_select(void *data)
2092 PostponedSelectData *psdata = (PostponedSelectData *)data;
2093 debug_print("trying again\n");
2094 psdata->folderview->open_folder = TRUE;
2095 main_window_cursor_normal(psdata->folderview->mainwin);
2096 STATUSBAR_POP(psdata->folderview->mainwin);
2097 folderview_selected(psdata->ctree, psdata->row,
2098 psdata->column, psdata->folderview);
2103 void folderview_close_opened(FolderView *folderview)
2105 if (folderview->opened) {
2106 FolderItem *olditem;
2108 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2109 folderview->opened);
2111 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2112 olditem->path ? olditem->path:olditem->name);
2113 /* will be null if we just moved the previously opened folder */
2114 STATUSBAR_PUSH(folderview->mainwin, buf);
2115 main_window_cursor_wait(folderview->mainwin);
2117 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2118 summary_show(folderview->summaryview, NULL);
2119 folder_item_close(olditem);
2120 main_window_cursor_normal(folderview->mainwin);
2121 STATUSBAR_POP(folderview->mainwin);
2125 if (folderview->opened &&
2126 !GTK_CTREE_ROW(folderview->opened)->children)
2127 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2129 folderview->opened = NULL;
2131 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2132 gint column, FolderView *folderview)
2134 static gboolean can_select = TRUE; /* exclusive lock */
2139 GtkCTreeNode *old_opened = folderview->opened;
2141 folderview->selected = row;
2143 debug_print("newly selected %p, opened %p\n", folderview->selected,
2144 folderview->opened);
2145 if (folderview->opened == row) {
2146 folderview->open_folder = FALSE;
2151 if (!can_select || summary_is_locked(folderview->summaryview)) {
2152 if (folderview->opened) {
2153 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2154 gtk_ctree_select(ctree, folderview->opened);
2156 folderview->open_folder = FALSE;
2161 if (!folderview->open_folder) {
2165 item = gtk_ctree_node_get_row_data(ctree, row);
2168 folderview->open_folder = FALSE;
2174 /* Save cache for old folder */
2175 /* We don't want to lose all caches if sylpheed crashed */
2176 /* resets folderview->opened to NULL */
2177 folderview_close_opened(folderview);
2179 /* CLAWS: set compose button type: news folder items
2180 * always have a news folder as parent */
2182 toolbar_set_compose_button
2183 (folderview->mainwin->toolbar,
2184 FOLDER_TYPE(item->folder) == F_NEWS ?
2185 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2188 debug_print("Folder %s is selected\n", item->path);
2190 if (!GTK_CTREE_ROW(row)->children)
2191 gtk_ctree_expand(ctree, row);
2193 /* ungrab the mouse event */
2194 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2195 gtk_grab_remove(GTK_WIDGET(ctree));
2196 if (gdk_pointer_is_grabbed())
2197 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2201 /* TODO: wwp: avoid displaying (null) in the status bar */
2202 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2203 item->path : "(null)");
2204 debug_print("%s\n", buf);
2205 STATUSBAR_PUSH(folderview->mainwin, buf);
2208 main_window_cursor_wait(folderview->mainwin);
2210 res = folder_item_open(item);
2211 if (res == -1 && item->no_select == FALSE) {
2212 main_window_cursor_normal(folderview->mainwin);
2213 STATUSBAR_POP(folderview->mainwin);
2215 alertpanel_error(_("Folder could not be opened."));
2217 folderview->open_folder = FALSE;
2221 } else if (res == -2 && item->no_select == FALSE) {
2222 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2223 data->ctree = ctree;
2225 data->column = column;
2226 data->folderview = folderview;
2227 debug_print("postponing open of %s till end of scan\n",
2228 item->path ? item->path:item->name);
2229 folderview->open_folder = FALSE;
2231 g_timeout_add(500, postpone_select, data);
2236 main_window_cursor_normal(folderview->mainwin);
2239 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2240 opened = summary_show(folderview->summaryview, item);
2242 folder_clean_cache_memory(item);
2245 gtkut_ctree_set_focus_row(ctree, old_opened);
2246 gtk_ctree_select(ctree, old_opened);
2247 folderview->opened = old_opened;
2249 folderview->opened = row;
2250 if (gtk_ctree_node_is_visible(ctree, row)
2251 != GTK_VISIBILITY_FULL)
2252 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2255 STATUSBAR_POP(folderview->mainwin);
2257 folderview->open_folder = FALSE;
2262 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2263 FolderView *folderview)
2267 item = gtk_ctree_node_get_row_data(ctree, node);
2268 g_return_if_fail(item != NULL);
2269 item->collapsed = FALSE;
2270 folderview_update_node(folderview, node);
2273 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2274 FolderView *folderview)
2278 item = gtk_ctree_node_get_row_data(ctree, node);
2279 g_return_if_fail(item != NULL);
2280 item->collapsed = TRUE;
2281 folderview_update_node(folderview, node);
2284 static void folderview_popup_close(GtkMenuShell *menu_shell,
2285 FolderView *folderview)
2287 if (!folderview->opened) return;
2289 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2292 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2293 FolderView *folderview)
2295 FolderColumnType type = folderview->col_state[column].type;
2297 prefs_common.folder_col_size[type] = width;
2300 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2304 folderview_create_folder_node(folderview, item);
2306 if (!item || !item->folder || !item->folder->node)
2309 srcnode = item->folder->node;
2310 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2311 srcnode = srcnode->children;
2312 while (srcnode != NULL) {
2313 if (srcnode && srcnode->data) {
2314 FolderItem *next_item = (FolderItem*) srcnode->data;
2315 folderview_create_folder_node_recursive(folderview, next_item);
2317 srcnode = srcnode->next;
2321 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2323 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2324 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2325 GtkCTreeNode *node, *parent_node;
2326 gint *col_pos = folderview->col_pos;
2327 FolderItemUpdateData hookdata;
2329 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2330 if (parent_node == NULL)
2333 gtk_clist_freeze(GTK_CLIST(ctree));
2335 text[col_pos[F_COL_FOLDER]] = item->name;
2336 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2338 folderxpm, folderxpmmask,
2339 folderopenxpm, folderopenxpmmask,
2341 gtk_ctree_expand(ctree, parent_node);
2342 gtk_ctree_node_set_row_data(ctree, node, item);
2344 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2345 folderview_sort_folders(folderview, parent_node, item->folder);
2347 hookdata.item = item;
2348 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2349 hookdata.msg = NULL;
2350 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2352 gtk_clist_thaw(GTK_CLIST(ctree));
2355 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2358 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2360 GSList *mlist = NULL;
2362 FolderItem *special_trash = NULL;
2365 if (!folderview->selected) return;
2366 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2367 g_return_if_fail(item != NULL);
2368 g_return_if_fail(item->folder != NULL);
2370 if (NULL != (ac = account_find_from_item(item)))
2371 special_trash = account_get_special_folder(ac, F_TRASH);
2373 if (item != item->folder->trash && item != special_trash
2374 && !folder_has_parent_of_type(item, F_TRASH)) return;
2376 if (prefs_common.ask_on_clean) {
2377 if (alertpanel(_("Empty trash"),
2378 _("Delete all messages in trash?"),
2379 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2383 mlist = folder_item_get_msg_list(item);
2385 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2386 MsgInfo * msginfo = (MsgInfo *) cur->data;
2387 if (MSG_IS_LOCKED(msginfo->flags))
2389 /* is it partially received? (partial_recv isn't cached) */
2390 if (msginfo->total_size != 0 &&
2391 msginfo->size != (off_t)msginfo->total_size)
2392 partial_mark_for_delete(msginfo);
2394 procmsg_msg_list_free(mlist);
2396 folder_item_remove_all_msg(item);
2399 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2402 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2404 FolderItem *special_queue = NULL;
2406 gchar *errstr = NULL;
2408 if (!folderview->selected) return;
2409 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2410 g_return_if_fail(item != NULL);
2411 g_return_if_fail(item->folder != NULL);
2413 if (NULL != (ac = account_find_from_item(item)))
2414 special_queue = account_get_special_folder(ac, F_QUEUE);
2416 if (item != item->folder->queue && item != special_queue
2417 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2419 if (procmsg_queue_is_empty(item))
2422 if (prefs_common.work_offline)
2423 if (alertpanel(_("Offline warning"),
2424 _("You're working offline. Override?"),
2425 GTK_STOCK_NO, GTK_STOCK_YES,
2426 NULL) != G_ALERTALTERNATE)
2429 /* ask for confirmation before sending queued messages only
2430 in online mode and if there is at least one message queued
2431 in any of the folder queue
2433 if (prefs_common.confirm_send_queued_messages) {
2434 if (!prefs_common.work_offline) {
2435 if (alertpanel(_("Send queued messages"),
2436 _("Send all queued messages?"),
2437 GTK_STOCK_CANCEL, _("_Send"),
2438 NULL) != G_ALERTALTERNATE)
2443 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2445 alertpanel_error_log(_("Some errors occurred while "
2446 "sending queued messages."));
2448 alertpanel_error_log(_("Some errors occurred "
2449 "while sending queued messages:\n%s"), errstr);
2455 static void folderview_search_cb(FolderView *folderview, guint action,
2458 summary_search(folderview->summaryview);
2461 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2464 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2467 if (!folderview->selected) return;
2469 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2470 g_return_if_fail(item != NULL);
2471 g_return_if_fail(item->folder != NULL);
2473 folder_item_apply_processing(item);
2476 static void folderview_property_cb(FolderView *folderview, guint action,
2479 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2482 if (!folderview->selected) return;
2484 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2485 g_return_if_fail(item != NULL);
2486 g_return_if_fail(item->folder != NULL);
2488 prefs_folder_item_open(item);
2491 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2493 GSList *list = NULL;
2494 GSList *done = NULL;
2495 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2497 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2498 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2499 && list->data != node) {
2500 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2501 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2504 for (list = done; list != NULL; list = g_slist_next(list)) {
2505 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2511 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2512 FolderItem *to_folder, gboolean copy)
2514 FolderItem *from_parent = NULL;
2515 FolderItem *new_folder = NULL;
2516 GtkCTreeNode *src_node = NULL;
2520 g_return_if_fail(folderview != NULL);
2521 g_return_if_fail(from_folder != NULL);
2522 g_return_if_fail(to_folder != NULL);
2524 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2525 from_parent = folder_item_parent(from_folder);
2527 if (prefs_common.warn_dnd) {
2528 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2529 _("Do you really want to make folder '%s' a subfolder of '%s' ?"),
2530 from_folder->name, to_folder->name);
2531 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2532 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2533 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2536 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2538 else if (status & G_ALERTDISABLE)
2539 prefs_common.warn_dnd = FALSE;
2542 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2543 from_folder->name, to_folder->name);
2544 STATUSBAR_PUSH(folderview->mainwin, buf);
2546 summary_clear_all(folderview->summaryview);
2547 folderview->opened = NULL;
2548 folderview->selected = NULL;
2549 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2551 main_window_cursor_wait(folderview->mainwin);
2553 statusbar_verbosity_set(FALSE);
2554 folder_item_update_freeze();
2555 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2556 statusbar_verbosity_set(FALSE);
2557 main_window_cursor_normal(folderview->mainwin);
2558 STATUSBAR_POP(folderview->mainwin);
2559 folder_item_update_thaw();
2560 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2562 folderview_sort_folders(folderview,
2563 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2564 NULL, to_folder), new_folder->folder);
2565 folderview_select(folderview, new_folder);
2567 statusbar_verbosity_set(FALSE);
2568 main_window_cursor_normal(folderview->mainwin);
2569 STATUSBAR_POP(folderview->mainwin);
2570 folder_item_update_thaw();
2572 case F_MOVE_FAILED_DEST_IS_PARENT:
2573 alertpanel_error(_("Source and destination are the same."));
2575 case F_MOVE_FAILED_DEST_IS_CHILD:
2576 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2577 _("Can't move a folder to one of its children."));
2579 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2580 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2583 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2588 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2591 static gint folderview_clist_compare(GtkCList *clist,
2592 gconstpointer ptr1, gconstpointer ptr2)
2594 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2595 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2597 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2599 return item1->order - item2->order;
2602 // if only one folder has an order it comes first
2603 if (item1->order > 0)
2607 if (item2->order > 0)
2613 return (item2->name != NULL);
2617 return g_utf8_collate(item1->name, item2->name);
2620 static void folderview_processing_cb(FolderView *folderview, guint action,
2623 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2627 if (!folderview->selected) return;
2629 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2630 g_return_if_fail(item != NULL);
2631 g_return_if_fail(item->folder != NULL);
2633 id = folder_item_get_identifier(item);
2634 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2637 prefs_filtering_open(&item->prefs->processing, title,
2638 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2642 void folderview_set_target_folder_color(gint color_op)
2646 FolderView *folderview;
2648 for (list = folderview_list; list != NULL; list = list->next) {
2649 folderview = (FolderView *)list->data;
2650 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2652 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2653 folderview->color_op;
2659 static gchar *last_font = NULL;
2660 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2668 void folderview_reflect_prefs(void)
2670 gboolean update_font = TRUE;
2671 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2672 FolderItem *item = folderview_get_selected_item(folderview);
2673 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2674 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2675 gint height = pos->value;
2677 if (last_font && !strcmp(last_font, NORMAL_FONT))
2678 update_font = FALSE;
2681 last_font = g_strdup(NORMAL_FONT);
2684 normal_style = normal_color_style = bold_style =
2685 bold_color_style = bold_tgtfold_style = NULL;
2687 folderview_init(folderview);
2689 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2690 folderview_column_set_titles(folderview);
2691 folderview_set_all();
2693 g_signal_handlers_block_by_func
2694 (G_OBJECT(folderview->ctree),
2695 G_CALLBACK(folderview_selected), folderview);
2698 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2699 GTK_CTREE(folderview->ctree), NULL, item);
2701 folderview_select(folderview, item);
2702 folderview->open_folder = FALSE;
2703 folderview->selected = node;
2706 g_signal_handlers_unblock_by_func
2707 (G_OBJECT(folderview->ctree),
2708 G_CALLBACK(folderview_selected), folderview);
2710 pos = gtk_scrolled_window_get_vadjustment(
2711 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2712 gtk_adjustment_set_value(pos, height);
2713 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2716 static void drag_state_stop(FolderView *folderview)
2718 if (folderview->drag_timer)
2719 g_source_remove(folderview->drag_timer);
2720 folderview->drag_timer = 0;
2721 folderview->drag_node = NULL;
2724 static gint folderview_defer_expand(FolderView *folderview)
2726 if (folderview->drag_node) {
2727 folderview_recollapse_nodes(folderview, folderview->drag_node);
2728 if (folderview->drag_item->collapsed) {
2729 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2730 folderview->nodes_to_recollapse = g_slist_append
2731 (folderview->nodes_to_recollapse, folderview->drag_node);
2734 folderview->drag_item = NULL;
2735 folderview->drag_timer = 0;
2739 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2741 /* the idea is that we call drag_state_start() whenever we want expansion to
2742 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2743 * we need to call drag_state_stop() */
2744 drag_state_stop(folderview);
2745 /* request expansion */
2746 if (0 != (folderview->drag_timer = g_timeout_add
2747 (prefs_common.hover_timeout,
2748 (GtkFunction)folderview_defer_expand,
2750 folderview->drag_node = node;
2751 folderview->drag_item = item;
2755 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2756 FolderView *folderview)
2758 GdkDragContext *context;
2760 g_return_if_fail(folderview != NULL);
2761 if (folderview->selected == NULL) return;
2762 if (folderview->nodes_to_recollapse)
2763 g_slist_free(folderview->nodes_to_recollapse);
2764 folderview->nodes_to_recollapse = NULL;
2765 context = gtk_drag_begin(widget, folderview->target_list,
2766 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2767 gtk_drag_set_icon_default(context);
2770 static void folderview_drag_data_get(GtkWidget *widget,
2771 GdkDragContext *drag_context,
2772 GtkSelectionData *selection_data,
2775 FolderView *folderview)
2779 gchar *source = NULL;
2780 if (info == TARGET_DUMMY) {
2781 for (cur = GTK_CLIST(folderview->ctree)->selection;
2782 cur != NULL; cur = cur->next) {
2783 item = gtk_ctree_node_get_row_data
2784 (GTK_CTREE(folderview->ctree),
2785 GTK_CTREE_NODE(cur->data));
2787 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2788 gtk_selection_data_set(selection_data,
2789 selection_data->target, 8,
2790 source, strlen(source));
2796 g_warning("unknown info %d\n", info);
2800 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2802 FolderUpdateData *hookdata;
2803 FolderView *folderview;
2807 folderview = (FolderView *) userdata;
2808 g_return_val_if_fail(hookdata != NULL, FALSE);
2809 g_return_val_if_fail(folderview != NULL, FALSE);
2811 ctree = folderview->ctree;
2812 g_return_val_if_fail(ctree != NULL, FALSE);
2814 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2815 folderview_create_folder_node(folderview, hookdata->item);
2816 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2817 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2818 NULL, folder_item_parent(hookdata->item));
2819 folderview_sort_folders(folderview, node, hookdata->folder);
2820 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2823 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2825 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2826 if (folderview->selected == node)
2827 folderview->selected = NULL;
2828 if (folderview->opened == node)
2829 folderview->opened = NULL;
2831 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2832 folderview_set(folderview);
2837 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2838 GdkDragContext *context,
2842 FolderView *folderview)
2845 FolderItem *item = NULL, *src_item = NULL;
2846 GtkCTreeNode *node = NULL;
2847 gboolean acceptable = FALSE;
2848 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2849 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2850 int height = (int)pos->page_size;
2851 int total_height = (int)pos->upper;
2852 int vpos = (int) pos->value;
2854 if (gtk_clist_get_selection_info
2855 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
2856 GtkWidget *srcwidget;
2858 if (y > height - 24 && height + vpos < total_height)
2859 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2861 if (y < 48 && y > 0)
2862 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2864 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2865 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2866 src_item = folderview->summaryview->folder_item;
2868 srcwidget = gtk_drag_get_source_widget(context);
2869 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2870 /* comes from summaryview */
2871 /* we are copying messages, so only accept folder items that are not
2872 the source item, are no root items and can copy messages */
2873 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2874 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2875 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2877 } else if (srcwidget == folderview->ctree) {
2878 /* comes from folderview */
2879 /* we are moving folder items, only accept folders that are not
2880 the source items and can copy messages and create folder items */
2881 if (item && item->folder && src_item && src_item != item &&
2882 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2883 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2884 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2885 || item->folder == src_item->folder))
2888 /* comes from another app */
2889 /* we are adding messages, so only accept folder items that are
2890 no root items and can copy messages */
2891 if (item && item->folder && folder_item_parent(item) != NULL
2892 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2893 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2898 if (acceptable || (src_item && src_item == item))
2899 drag_state_start(folderview, node, item);
2902 g_signal_handlers_block_by_func
2904 G_CALLBACK(folderview_selected), folderview);
2905 gtk_ctree_select(GTK_CTREE(widget), node);
2906 g_signal_handlers_unblock_by_func
2908 G_CALLBACK(folderview_selected), folderview);
2909 gdk_drag_status(context,
2910 (context->actions == GDK_ACTION_COPY ?
2911 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2913 if (folderview->opened)
2914 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2915 gdk_drag_status(context, 0, time);
2921 static void folderview_drag_leave_cb(GtkWidget *widget,
2922 GdkDragContext *context,
2924 FolderView *folderview)
2926 drag_state_stop(folderview);
2927 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2930 static void free_info (gpointer stuff, gpointer data)
2935 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2936 guint time, FolderItem *item)
2939 GSList *msglist = NULL;
2940 list = uri_list_extract_filenames(data);
2941 if (!(item && item->folder && folder_item_parent(item) != NULL
2942 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2944 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2945 debug_print("item doesn't fit\n");
2949 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2950 debug_print("list is empty\n");
2953 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2954 MsgFileInfo *info = NULL;
2956 if (file_is_email((gchar *)tmp->data)) {
2957 info = g_new0(MsgFileInfo, 1);
2958 info->msginfo = NULL;
2959 info->file = (gchar *)tmp->data;
2960 msglist = g_slist_prepend(msglist, info);
2961 debug_print("file is a mail\n");
2963 debug_print("file isn't a mail\n");
2967 msglist = g_slist_reverse(msglist);
2968 folder_item_add_msgs(item, msglist, FALSE);
2969 g_slist_foreach(msglist, free_info, NULL);
2970 g_slist_free(msglist);
2971 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2973 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2975 list_free_strings(list);
2979 static void folderview_drag_received_cb(GtkWidget *widget,
2980 GdkDragContext *drag_context,
2983 GtkSelectionData *data,
2986 FolderView *folderview)
2989 FolderItem *item = NULL, *src_item;
2992 if (info == TARGET_DUMMY) {
2993 drag_state_stop(folderview);
2994 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2995 /* comes from summaryview */
2996 if (gtk_clist_get_selection_info
2997 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
3000 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3001 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3002 src_item = folderview->summaryview->folder_item;
3004 if (item->no_select) {
3005 alertpanel_error(_("The destination folder can only be used to "
3006 "store subfolders."));
3009 /* re-check (due to acceptable possibly set for folder moves */
3010 if (!(item && item->folder && item->path && !item->no_select &&
3011 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3014 if (item && src_item) {
3015 switch (drag_context->action) {
3016 case GDK_ACTION_COPY:
3017 summary_copy_selected_to(folderview->summaryview, item);
3018 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3020 case GDK_ACTION_MOVE:
3021 case GDK_ACTION_DEFAULT:
3023 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3024 summary_copy_selected_to(folderview->summaryview, item);
3026 summary_move_selected_to(folderview->summaryview, item);
3027 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3030 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3032 /* comes from folderview */
3034 gboolean folder_is_normal = TRUE;
3035 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3037 source = data->data + 17;
3038 if (gtk_clist_get_selection_info
3039 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0
3041 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3044 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3045 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3046 src_item = folder_find_item_from_identifier(source);
3050 src_item->stype == F_NORMAL &&
3051 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3052 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3053 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3054 !folder_has_parent_of_type(src_item, F_TRASH);
3055 if (!item || !src_item || !folder_is_normal) {
3056 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3060 folderview_move_folder(folderview, src_item, item, copy);
3061 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3063 folderview->nodes_to_recollapse = NULL;
3064 } else if (info == TARGET_MAIL_URI_LIST) {
3065 if (gtk_clist_get_selection_info
3066 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
3069 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3071 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3072 debug_print("no node\n");
3075 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3077 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3078 debug_print("no item\n");
3081 folderview_finish_dnd(data->data, drag_context, time, item);
3085 static void folderview_drag_end_cb(GtkWidget *widget,
3086 GdkDragContext *drag_context,
3087 FolderView *folderview)
3089 drag_state_stop(folderview);
3090 g_slist_free(folderview->nodes_to_recollapse);
3091 folderview->nodes_to_recollapse = NULL;
3094 void folderview_register_popup(FolderViewPopup *fpopup)
3098 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3099 FolderView *folderview = folderviews->data;
3100 GtkItemFactory *factory;
3102 factory = create_ifactory(folderview, fpopup);
3103 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3105 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3108 void folderview_unregister_popup(FolderViewPopup *fpopup)
3112 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3113 FolderView *folderview = folderviews->data;
3115 g_hash_table_remove(folderview->popups, fpopup->klass);
3117 g_hash_table_remove(folderview_popups, fpopup->klass);