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_("/Run 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_new;
381 GtkWidget *label_unread;
382 GtkWidget *label_total;
384 GtkWidget *hbox_unread;
385 GtkWidget *hbox_total;
386 gint *col_pos = folderview->col_pos;
388 debug_print("setting titles...\n");
389 gtk_widget_realize(folderview->ctree);
390 gtk_widget_show_all(folderview->scrolledwin);
392 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
393 * instead text (text overflows making them unreadable and ugly) */
394 stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW,
395 &newxpm, &newxpmmask);
396 stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD,
397 &unreadxpm, &unreadxpmmask);
398 stock_pixmap_gdk(ctree, STOCK_PIXMAP_READ,
399 &readxpm, &readxpmmask);
401 label_new = gtk_image_new_from_pixmap(newxpm, newxpmmask);
402 label_unread = gtk_image_new_from_pixmap(unreadxpm, unreadxpmmask);
403 label_total = gtk_image_new_from_pixmap(readxpm, readxpmmask);
405 gtk_clist_column_titles_active(GTK_CLIST(ctree));
407 hbox_new = gtk_hbox_new(FALSE, 4);
408 hbox_unread = gtk_hbox_new(FALSE, 4);
409 hbox_total = gtk_hbox_new(FALSE, 4);
412 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
413 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
414 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
415 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
416 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
417 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
419 gtk_widget_show_all(hbox_new);
420 gtk_widget_show_all(hbox_unread);
421 gtk_widget_show_all(hbox_total);
423 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_NEW],hbox_new);
424 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
425 gtk_clist_set_column_widget(GTK_CLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
427 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
428 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
429 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
432 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
434 FolderView *folderview = (FolderView *)data;
435 GdkEventButton event;
436 if (folderview_get_selected_item(folderview) == NULL)
440 event.time = gtk_get_current_event_time();
442 folderview_set_sens_and_popup_menu(folderview, -1,
449 static GtkWidget *folderview_ctree_create(FolderView *folderview)
453 FolderColumnState *col_state;
454 FolderColumnType type;
455 gchar *titles[N_FOLDER_COLS];
457 GtkWidget *scrolledwin = folderview->scrolledwin;
459 debug_print("creating tree...\n");
460 memset(titles, 0, sizeof(titles));
462 col_state = prefs_folder_column_get_config();
463 memset(titles, 0, sizeof(titles));
465 col_pos = folderview->col_pos;
467 for (i = 0; i < N_FOLDER_COLS; i++) {
468 folderview->col_state[i] = col_state[i];
469 type = col_state[i].type;
473 titles[col_pos[F_COL_FOLDER]] = _("Folder");
474 titles[col_pos[F_COL_NEW]] = _("New");
475 titles[col_pos[F_COL_UNREAD]] = _("Unread");
476 /* TRANSLATORS: This in Number sign in American style */
477 titles[col_pos[F_COL_TOTAL]] = _("#");
479 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
482 if (prefs_common.show_col_headers == FALSE)
483 gtk_clist_column_titles_hide(GTK_CLIST(ctree));
486 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_BROWSE);
487 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[F_COL_NEW],
489 gtk_clist_set_column_justification(GTK_CLIST(ctree),
490 col_pos[F_COL_UNREAD],
492 gtk_clist_set_column_justification(GTK_CLIST(ctree),
493 col_pos[F_COL_TOTAL],
495 if (prefs_common.enable_dotted_lines) {
496 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
497 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
498 GTK_CTREE_EXPANDER_SQUARE);
500 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
501 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
502 GTK_CTREE_EXPANDER_TRIANGLE);
505 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
506 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
508 gtk_ctree_set_indent(GTK_CTREE(ctree), CTREE_INDENT);
509 gtk_clist_set_compare_func(GTK_CLIST(ctree), folderview_clist_compare);
511 /* don't let title buttons take key focus */
512 for (i = 0; i < N_FOLDER_COLS; i++) {
513 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
515 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[i],
516 prefs_common.folder_col_size[i]);
517 gtk_clist_set_column_visibility
518 (GTK_CLIST(ctree), i, col_state[i].visible);
521 g_signal_connect(G_OBJECT(ctree), "key_press_event",
522 G_CALLBACK(folderview_key_pressed),
524 g_signal_connect(G_OBJECT(ctree), "button_press_event",
525 G_CALLBACK(folderview_button_pressed),
528 g_signal_connect(G_OBJECT(ctree), "popup-menu",
529 G_CALLBACK(folderview_popup_menu), folderview);
531 gtk_widget_tap_and_hold_setup(GTK_WIDGET(ctree), NULL, NULL,
532 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
533 g_signal_connect(G_OBJECT(ctree), "tap-and-hold",
534 G_CALLBACK(folderview_popup_menu), folderview);
536 g_signal_connect(G_OBJECT(ctree), "button_release_event",
537 G_CALLBACK(folderview_button_released),
539 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
540 G_CALLBACK(folderview_selected), folderview);
542 /* drag-n-dropping folders on maemo is impractical as this
543 * opens the folder almost everytime */
544 g_signal_connect(G_OBJECT(ctree), "start_drag",
545 G_CALLBACK(folderview_start_drag), folderview);
547 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
548 G_CALLBACK(folderview_drag_data_get),
551 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
552 G_CALLBACK(folderview_tree_expanded),
554 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
555 G_CALLBACK(folderview_tree_collapsed),
558 g_signal_connect(G_OBJECT(ctree), "resize_column",
559 G_CALLBACK(folderview_col_resized),
563 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
564 folderview_drag_types, 2,
565 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
566 g_signal_connect(G_OBJECT(ctree), "drag_motion",
567 G_CALLBACK(folderview_drag_motion_cb),
569 g_signal_connect(G_OBJECT(ctree), "drag_leave",
570 G_CALLBACK(folderview_drag_leave_cb),
572 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
573 G_CALLBACK(folderview_drag_received_cb),
575 g_signal_connect(G_OBJECT(ctree), "drag_end",
576 G_CALLBACK(folderview_drag_end_cb),
579 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
584 void folderview_set_column_order(FolderView *folderview)
587 FolderItem *item = folderview_get_selected_item(folderview);
588 GtkWidget *scrolledwin = folderview->scrolledwin;
590 debug_print("recreating tree...\n");
591 gtk_widget_destroy(folderview->ctree);
593 folderview->ctree = ctree = folderview_ctree_create(folderview);
594 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
595 GTK_CLIST(ctree)->hadjustment);
596 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
597 GTK_CLIST(ctree)->vadjustment);
598 gtk_widget_show(ctree);
600 folderview_set(folderview);
601 folderview_column_set_titles(folderview);
603 folderview_select(folderview,item);
606 FolderView *folderview_create(void)
608 FolderView *folderview;
609 GtkWidget *scrolledwin;
612 debug_print("Creating folder view...\n");
613 folderview = g_new0(FolderView, 1);
615 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
616 gtk_scrolled_window_set_policy
617 (GTK_SCROLLED_WINDOW(scrolledwin),
618 GTK_POLICY_AUTOMATIC,
619 prefs_common.folderview_vscrollbar_policy);
620 gtk_widget_set_size_request(scrolledwin,
621 prefs_common.folderview_width,
622 prefs_common.folderview_height);
624 folderview->scrolledwin = scrolledwin;
625 ctree = folderview_ctree_create(folderview);
627 /* create popup factories */
628 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
629 g_hash_table_foreach(folderview_popups, create_ifactories, folderview);
631 folderview->ctree = ctree;
633 folderview->folder_update_callback_id =
634 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
635 folderview->folder_item_update_callback_id =
636 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
638 gtk_widget_show_all(scrolledwin);
640 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
641 folderview_list = g_list_append(folderview_list, folderview);
642 folderview->deferred_refresh_id = -1;
647 void folderview_init(FolderView *folderview)
649 GtkWidget *ctree = folderview->ctree;
652 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm, &inboxxpmmask);
653 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm, &inboxhrmxpmmask);
654 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm, &inboxopenxpmmask);
655 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm, &inboxopenhrmxpmmask);
656 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm, &outboxxpmmask);
657 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm, &outboxhrmxpmmask);
658 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm, &outboxopenxpmmask);
659 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm, &outboxopenhrmxpmmask);
660 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm, &folderxpmmask);
661 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm, &folderhrmxpmmask);
662 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm, &folderopenxpmmask);
663 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm, &folderopenhrmxpmmask);
664 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm, &trashopenxpmmask);
665 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm, &trashopenhrmxpmmask);
666 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm, &trashxpmmask);
667 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm, &trashhrmxpmmask);
668 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm, &queuexpmmask);
669 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm, &queuehrmxpmmask);
670 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm, &queueopenxpmmask);
671 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm, &queueopenhrmxpmmask);
672 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm, &draftsxpmmask);
673 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm, &draftsopenxpmmask);
674 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm, &noselectxpmmask);
676 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm, &m_inboxxpmmask);
677 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm, &m_inboxhrmxpmmask);
678 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm, &m_inboxopenxpmmask);
679 stock_pixmap_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm, &m_inboxopenhrmxpmmask);
680 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm, &m_outboxxpmmask);
681 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm, &m_outboxhrmxpmmask);
682 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm, &m_outboxopenxpmmask);
683 stock_pixmap_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm, &m_outboxopenhrmxpmmask);
684 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm, &m_folderxpmmask);
685 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm, &m_folderhrmxpmmask);
686 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm, &m_folderopenxpmmask);
687 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm, &m_folderopenhrmxpmmask);
688 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm, &m_trashopenxpmmask);
689 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm, &m_trashopenhrmxpmmask);
690 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm, &m_trashxpmmask);
691 stock_pixmap_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm, &m_trashhrmxpmmask);
692 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm, &m_queuexpmmask);
693 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm, &m_queuehrmxpmmask);
694 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm, &m_queueopenxpmmask);
695 stock_pixmap_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm, &m_queueopenhrmxpmmask);
696 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm, &m_draftsxpmmask);
697 stock_pixmap_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm, &m_draftsopenxpmmask);
700 PangoFontDescription *font_desc;
701 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
702 font_desc = pango_font_description_from_string(NORMAL_FONT);
704 if (normal_style->font_desc)
705 pango_font_description_free
706 (normal_style->font_desc);
707 normal_style->font_desc = font_desc;
709 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
710 normal_color_style = gtk_style_copy(normal_style);
711 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
713 gtk_widget_set_style(ctree, normal_style);
717 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
718 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
719 pango_font_description_set_weight
720 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
721 bold_color_style = gtk_style_copy(bold_style);
722 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
724 bold_tgtfold_style = gtk_style_copy(bold_style);
725 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
729 static gboolean folderview_defer_set(gpointer data)
731 FolderView *folderview = (FolderView *)data;
732 MainWindow *mainwin = folderview->mainwin;
736 if (mainwin->lock_count)
739 printf("doing deferred folderview_set now\n");
740 folderview_set(folderview);
742 folderview->deferred_refresh_id = -1;
746 void folderview_set(FolderView *folderview)
748 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
749 MainWindow *mainwin = folderview->mainwin;
750 FolderItem *sel_item = NULL, *op_item = NULL;
755 if (mainwin->lock_count) {
756 if (folderview->deferred_refresh_id == -1)
757 folderview->deferred_refresh_id =
758 g_timeout_add(500, folderview_defer_set, folderview);
759 printf("deferred folderview_set\n");
764 debug_print("Setting folder info...\n");
765 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
767 main_window_cursor_wait(mainwin);
769 if (folderview->selected)
770 sel_item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
771 if (folderview->opened)
772 op_item = gtk_ctree_node_get_row_data(ctree, folderview->opened);
774 folderview->selected = NULL;
775 folderview->opened = NULL;
777 gtk_clist_freeze(GTK_CLIST(ctree));
778 gtk_clist_clear(GTK_CLIST(ctree));
780 folderview_set_folders(folderview);
783 folderview->selected = gtk_ctree_find_by_row_data(ctree, NULL, sel_item);
785 folderview->opened = gtk_ctree_find_by_row_data(ctree, NULL, op_item);
787 gtk_clist_thaw(GTK_CLIST(ctree));
788 main_window_cursor_normal(mainwin);
789 STATUSBAR_POP(mainwin);
793 void folderview_set_all(void)
797 for (list = folderview_list; list != NULL; list = list->next)
798 folderview_set((FolderView *)list->data);
801 void folderview_select(FolderView *folderview, FolderItem *item)
803 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
805 GtkCTreeNode *old_selected = folderview->selected;
809 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
810 if (node) folderview_select_node(folderview, node);
812 if (old_selected != node)
813 folder_update_op_count();
816 static void mark_all_read_cb(FolderView *folderview, guint action,
822 item = folderview_get_selected_item(folderview);
826 if (folderview->summaryview->folder_item != item
827 && prefs_common.ask_mark_all_read) {
828 val = alertpanel_full(_("Mark all as read"),
829 _("Do you really want to mark all mails in this "
830 "folder as read ?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
831 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
833 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
835 else if (val & G_ALERTDISABLE)
836 prefs_common.ask_mark_all_read = FALSE;
839 summary_lock(folderview->summaryview);
840 folder_item_update_freeze();
841 if (folderview->summaryview->folder_item == item)
842 summary_freeze(folderview->summaryview);
843 folderutils_mark_all_read(item);
844 if (folderview->summaryview->folder_item == item)
845 summary_thaw(folderview->summaryview);
846 folder_item_update_thaw();
847 summary_unlock(folderview->summaryview);
850 static void folderview_select_node(FolderView *folderview, GtkCTreeNode *node)
852 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
854 g_return_if_fail(node != NULL);
856 if (folderview->open_folder) {
860 folderview->open_folder = TRUE;
861 gtkut_ctree_set_focus_row(ctree, node);
862 gtk_ctree_select(ctree, node);
863 if (folderview->summaryview->folder_item &&
864 folderview->summaryview->folder_item->total_msgs > 0)
865 summary_grab_focus(folderview->summaryview);
867 gtk_widget_grab_focus(folderview->ctree);
869 gtkut_ctree_expand_parent_all(ctree, node);
872 void folderview_unselect(FolderView *folderview)
874 if (folderview->opened && !GTK_CTREE_ROW(folderview->opened)->children)
876 (GTK_CTREE(folderview->ctree), folderview->opened);
878 folderview->selected = folderview->opened = NULL;
881 static GtkCTreeNode *folderview_find_next_marked(GtkCTree *ctree,
887 node = gtkut_ctree_node_next(ctree, node);
889 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
891 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
892 item = gtk_ctree_node_get_row_data(ctree, node);
893 if (item && item->marked_msgs > 0 && item->stype != F_TRASH)
900 void folderview_select_next_marked(FolderView *folderview)
902 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
903 GtkCTreeNode *node = NULL;
904 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
905 gboolean last_open = prefs_common.always_show_msg;
907 prefs_common.summary_select_prio[0] = ACTION_MARKED;
908 prefs_common.always_show_msg = TRUE;
910 if ((node = folderview_find_next_marked(ctree, folderview->opened))
912 folderview_select_node(folderview, node);
916 if (!folderview->opened ||
917 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
920 /* search again from the first node */
921 if ((node = folderview_find_next_marked(ctree, NULL)) != NULL)
922 folderview_select_node(folderview, node);
925 prefs_common.summary_select_prio[0] = last_summary_select_prio;
926 prefs_common.always_show_msg = last_open;
929 static GtkCTreeNode *folderview_find_next_unread(GtkCTree *ctree,
935 node = gtkut_ctree_node_next(ctree, node);
937 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
939 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
940 item = gtk_ctree_node_get_row_data(ctree, node);
941 if (item && item->unread_msgs > 0 && item->stype != F_TRASH)
948 void folderview_select_next_unread(FolderView *folderview, gboolean force_open)
950 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
951 GtkCTreeNode *node = NULL;
952 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
953 gboolean last_open = prefs_common.always_show_msg;
955 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
956 prefs_common.always_show_msg = force_open ? TRUE : last_open;
958 if ((node = folderview_find_next_unread(ctree, folderview->opened))
960 folderview_select_node(folderview, node);
964 if (!folderview->opened ||
965 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
968 /* search again from the first node */
969 if ((node = folderview_find_next_unread(ctree, NULL)) != NULL)
970 folderview_select_node(folderview, node);
973 prefs_common.summary_select_prio[0] = last_summary_select_prio;
974 prefs_common.always_show_msg = last_open;
977 static GtkCTreeNode *folderview_find_next_new(GtkCTree *ctree,
983 node = gtkut_ctree_node_next(ctree, node);
985 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
987 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
988 item = gtk_ctree_node_get_row_data(ctree, node);
989 if (item && item->new_msgs > 0 && item->stype != F_TRASH)
996 void folderview_select_next_new(FolderView *folderview)
998 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
999 GtkCTreeNode *node = NULL;
1000 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1001 gboolean last_open = prefs_common.always_show_msg;
1003 prefs_common.summary_select_prio[0] = ACTION_NEW;
1004 prefs_common.always_show_msg = TRUE;
1006 if ((node = folderview_find_next_new(ctree, folderview->opened))
1008 folderview_select_node(folderview, node);
1012 if (!folderview->opened ||
1013 folderview->opened == GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list)) {
1016 /* search again from the first node */
1017 if ((node = folderview_find_next_new(ctree, NULL)) != NULL)
1018 folderview_select_node(folderview, node);
1021 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1022 prefs_common.always_show_msg = last_open;
1025 FolderItem *folderview_get_selected_item(FolderView *folderview)
1027 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1029 if (!folderview->selected) return NULL;
1030 return gtk_ctree_node_get_row_data(ctree, folderview->selected);
1033 static void folderview_set_folders(FolderView *folderview)
1036 list = folder_get_list();
1038 for (; list != NULL; list = list->next) {
1039 folderview_append_folder(folderview, FOLDER(list->data));
1043 static gchar *get_scan_str(FolderItem *item)
1046 return g_strdup_printf(_("Scanning folder %s%c%s ..."),
1047 item->folder->name, G_DIR_SEPARATOR,
1050 return g_strdup_printf(_("Scanning folder %s ..."),
1051 item->folder->name);
1053 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1057 for (list = folderview_list; list != NULL; list = list->next) {
1058 FolderView *folderview = (FolderView *)list->data;
1059 MainWindow *mainwin = folderview->mainwin;
1060 gchar *str = get_scan_str(item);
1062 STATUSBAR_PUSH(mainwin, str);
1063 STATUSBAR_POP(mainwin);
1068 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1071 MainWindow *mainwin = mainwindow_get_mainwindow();
1072 FolderView *folderview = NULL;
1073 GtkAdjustment *pos = NULL;
1076 g_return_if_fail(folder != NULL);
1078 if (!folder->klass->scan_tree) return;
1081 alertpanel_full(_("Rebuild folder tree"),
1082 _("Rebuilding the folder tree will remove "
1083 "local caches. Do you want to continue?"),
1084 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1085 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1086 != G_ALERTALTERNATE) {
1092 window = label_window_create(_("Rebuilding folder tree..."));
1094 window = label_window_create(_("Scanning folder tree..."));
1097 folderview = mainwin->folderview;
1100 pos = gtk_scrolled_window_get_vadjustment(
1101 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1102 height = pos->value;
1105 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1106 folder_scan_tree(folder, rebuild);
1107 folder_set_ui_func(folder, NULL, NULL);
1109 folderview_set_all();
1112 pos = gtk_scrolled_window_get_vadjustment(
1113 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1114 gtk_adjustment_set_value(pos, height);
1116 label_window_destroy(window);
1120 void folderview_fast_rescan_tree(Folder *folder)
1123 MainWindow *mainwin = mainwindow_get_mainwindow();
1124 FolderView *folderview = NULL;
1125 GtkAdjustment *pos = NULL;
1128 g_return_if_fail(folder != NULL);
1130 if (!folder->klass->scan_tree) return;
1134 window = label_window_create(_("Scanning folder tree..."));
1137 folderview = mainwin->folderview;
1140 pos = gtk_scrolled_window_get_vadjustment(
1141 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1142 height = pos->value;
1145 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1146 folder_fast_scan_tree(folder);
1147 folder_set_ui_func(folder, NULL, NULL);
1149 folderview_set_all();
1152 pos = gtk_scrolled_window_get_vadjustment(
1153 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1154 gtk_adjustment_set_value(pos, height);
1156 label_window_destroy(window);
1160 /** folderview_check_new()
1161 * Scan and update the folder and return the
1162 * count the number of new messages since last check.
1163 * \param folder the folder to check for new messages
1164 * \return the number of new messages since last check
1166 gint folderview_check_new(Folder *folder)
1170 FolderView *folderview;
1174 gint former_new_msgs = 0;
1175 gint former_new = 0, former_unread = 0, former_total;
1177 for (list = folderview_list; list != NULL; list = list->next) {
1178 folderview = (FolderView *)list->data;
1179 ctree = GTK_CTREE(folderview->ctree);
1182 main_window_lock(folderview->mainwin);
1184 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1185 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1187 item = gtk_ctree_node_get_row_data(ctree, node);
1188 if (!item || !item->path || !item->folder) continue;
1189 if (item->no_select) continue;
1190 if (folder && folder != item->folder) continue;
1191 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1192 if (!item->prefs->newmailcheck) continue;
1193 if (item->processing_pending == TRUE) {
1194 debug_print("skipping %s, processing pending\n",
1195 item->path ? item->path : item->name);
1199 str = get_scan_str(item);
1201 STATUSBAR_PUSH(folderview->mainwin, str);
1205 folderview_scan_tree_func(item->folder, item, NULL);
1206 former_new = item->new_msgs;
1207 former_unread = item->unread_msgs;
1208 former_total = item->total_msgs;
1210 if (item->folder->klass->scan_required &&
1211 (item->folder->klass->scan_required(item->folder, item) ||
1212 item->folder->inbox == item ||
1213 item->opened == TRUE ||
1214 item->processing_pending == TRUE)) {
1215 if (folder_item_scan(item) < 0) {
1217 summaryview_unlock(folderview->summaryview, item);
1218 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1219 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1220 item->path ? item->path:item->name);
1221 STATUSBAR_POP(folderview->mainwin);
1223 } else if (!FOLDER_IS_LOCAL(folder)) {
1224 STATUSBAR_POP(folderview->mainwin);
1229 } else if (!item->folder->klass->scan_required) {
1230 if (folder_item_scan(item) < 0) {
1231 summaryview_unlock(folderview->summaryview, item);
1232 if (folder && !FOLDER_IS_LOCAL(folder)) {
1233 STATUSBAR_POP(folderview->mainwin);
1238 if (former_new != item->new_msgs ||
1239 former_unread != item->unread_msgs ||
1240 former_total != item->total_msgs)
1241 folderview_update_node(folderview, node);
1243 new_msgs += item->new_msgs;
1244 former_new_msgs += former_new;
1245 STATUSBAR_POP(folderview->mainwin);
1248 main_window_unlock(folderview->mainwin);
1252 folder_write_list();
1253 /* Number of new messages since last check is the just the difference
1254 * between former_new_msgs and new_msgs. If new_msgs is less than
1255 * former_new_msgs, that would mean another session accessed the folder
1256 * and the result is not well defined.
1258 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1262 void folderview_check_new_all(void)
1266 FolderView *folderview;
1268 folderview = (FolderView *)folderview_list->data;
1271 main_window_lock(folderview->mainwin);
1272 window = label_window_create
1273 (_("Checking for new messages in all folders..."));
1275 list = folder_get_list();
1276 for (; list != NULL; list = list->next) {
1277 Folder *folder = list->data;
1279 folderview_check_new(folder);
1282 folder_write_list();
1283 folderview_set_all();
1285 label_window_destroy(window);
1286 main_window_unlock(folderview->mainwin);
1290 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1296 if (!item || !item->folder || !item->folder->node)
1299 node = item->folder->node;
1301 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1302 node = node->children;
1305 (item->new_msgs > 0 ||
1306 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1310 while (node != NULL) {
1311 if (node && node->data) {
1312 FolderItem *next_item = (FolderItem*) node->data;
1314 if (folderview_have_new_children_sub(folderview,
1323 static gboolean folderview_have_new_children(FolderView *folderview,
1326 return folderview_have_new_children_sub(folderview, item, FALSE);
1329 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1335 if (!item || !item->folder || !item->folder->node)
1338 node = item->folder->node;
1340 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1341 node = node->children;
1344 (item->unread_msgs > 0 ||
1345 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1349 while (node != NULL) {
1350 if (node && node->data) {
1351 FolderItem *next_item = (FolderItem*) node->data;
1353 if (folderview_have_unread_children_sub(folderview,
1363 static gboolean folderview_have_unread_children(FolderView *folderview,
1366 return folderview_have_unread_children_sub(folderview, item, FALSE);
1369 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1375 if (!item || !item->folder || !item->folder->node)
1378 node = item->folder->node;
1380 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1381 node = node->children;
1383 if (in_sub && item->search_match){
1387 while (node != NULL) {
1388 if (node && node->data) {
1389 FolderItem *next_item = (FolderItem*) node->data;
1391 if (folderview_have_matching_children_sub(folderview,
1401 static gboolean folderview_have_matching_children(FolderView *folderview,
1404 return folderview_have_matching_children_sub(folderview, item, FALSE);
1407 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1413 if (!item || !item->folder || !item->folder->node)
1416 node = item->folder->node;
1418 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1419 node = node->children;
1421 if (item->marked_msgs != 0) {
1425 while (node != NULL) {
1426 if (node && node->data) {
1427 FolderItem *next_item = (FolderItem*) node->data;
1429 if (folderview_have_marked_children_sub(folderview,
1438 static gboolean folderview_have_marked_children(FolderView *folderview,
1441 return folderview_have_marked_children_sub(folderview, item, FALSE);
1444 static void folderview_update_node(FolderView *folderview, GtkCTreeNode *node)
1446 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1447 GtkStyle *style = NULL;
1448 GtkStyle *color_style = NULL;
1450 GdkPixmap *xpm, *openxpm;
1451 GdkBitmap *mask, *openmask;
1452 static GdkPixmap *searchicon;
1453 static GdkBitmap *searchmask;
1454 gboolean mark = FALSE;
1457 gboolean add_unread_mark;
1458 gboolean add_sub_match_mark;
1459 gboolean use_bold, use_color;
1460 gint *col_pos = folderview->col_pos;
1461 SpecialFolderItemType stype;
1463 item = gtk_ctree_node_get_row_data(ctree, node);
1464 g_return_if_fail(item != NULL);
1466 if (!GTK_CTREE_ROW(node)->expanded)
1467 mark = folderview_have_marked_children(folderview, item);
1469 mark = (item->marked_msgs != 0);
1471 stype = item->stype;
1472 if (stype == F_NORMAL) {
1473 if (folder_has_parent_of_type(item, F_TRASH))
1475 else if (folder_has_parent_of_type(item, F_DRAFT))
1477 else if (folder_has_parent_of_type(item, F_OUTBOX))
1479 else if (folder_has_parent_of_type(item, F_QUEUE))
1484 if (item->hide_read_msgs) {
1485 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1486 mask = mark?m_inboxhrmxpmmask:inboxhrmxpmmask;
1487 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1488 openmask = mark?m_inboxopenhrmxpmmask:inboxopenhrmxpmmask;
1490 xpm = mark?m_inboxxpm:inboxxpm;
1491 mask = mark?m_inboxxpmmask:inboxxpmmask;
1492 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1493 openmask = mark?m_inboxopenxpmmask:inboxopenxpmmask;
1497 if (item->hide_read_msgs) {
1498 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1499 mask = mark?m_outboxhrmxpmmask:outboxhrmxpmmask;
1500 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1501 openmask = mark?m_outboxopenhrmxpmmask:outboxopenhrmxpmmask;
1503 xpm = mark?m_outboxxpm:outboxxpm;
1504 mask = mark?m_outboxxpmmask:outboxxpmmask;
1505 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1506 openmask = mark?m_outboxopenxpmmask:outboxopenxpmmask;
1510 if (item->hide_read_msgs) {
1511 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1512 mask = mark?m_queuehrmxpmmask:queuehrmxpmmask;
1513 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1514 openmask = mark?m_queueopenhrmxpmmask:queueopenhrmxpmmask;
1516 xpm = mark?m_queuexpm:queuexpm;
1517 mask = mark?m_queuexpmmask:queuexpmmask;
1518 openxpm = mark?m_queueopenxpm:queueopenxpm;
1519 openmask = mark?m_queueopenxpmmask:queueopenxpmmask;
1523 if (item->hide_read_msgs) {
1524 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1525 mask = mark?m_trashhrmxpmmask:trashhrmxpmmask;
1526 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1527 openmask = mark?m_trashopenhrmxpmmask:trashopenhrmxpmmask;
1529 xpm = mark?m_trashxpm:trashxpm;
1530 mask = mark?m_trashxpmmask:trashxpmmask;
1531 openxpm = mark?m_trashopenxpm:trashopenxpm;
1532 openmask = mark?m_trashopenxpmmask:trashopenxpmmask;
1536 xpm = mark?m_draftsxpm:draftsxpm;
1537 mask = mark?m_draftsxpmmask:draftsxpmmask;
1538 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1539 openmask = mark?m_draftsopenxpmmask:draftsopenxpmmask;
1542 if (item->hide_read_msgs) {
1543 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1544 mask = mark?m_folderhrmxpmmask:folderhrmxpmmask;
1545 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1546 openmask = mark?m_folderopenhrmxpmmask:folderopenhrmxpmmask;
1548 xpm = mark?m_folderxpm:folderxpm;
1549 mask = mark?m_folderxpmmask:folderxpmmask;
1550 openxpm = mark?m_folderopenxpm:folderopenxpm;
1551 openmask = mark?m_folderopenxpmmask:folderopenxpmmask;
1555 if (item->no_select) {
1556 xpm = openxpm = noselectxpm;
1557 mask = openmask = noselectxpmmask;
1560 name = folder_item_get_name(item);
1562 if (!GTK_CTREE_ROW(node)->expanded) {
1563 add_unread_mark = folderview_have_unread_children(
1565 add_sub_match_mark = folderview_have_matching_children(
1568 add_unread_mark = FALSE;
1569 add_sub_match_mark = FALSE;
1572 if (item->search_match) {
1574 stock_pixmap_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1575 &searchicon, &searchmask);
1577 xpm = openxpm = searchicon;
1578 mask = openmask = searchmask;
1582 if (prefs_common.display_folder_unread) {
1583 if (folder_has_parent_of_type(item, F_QUEUE)) {
1584 /* only total_msgs matters here */
1585 if (item->total_msgs > 0) {
1586 /* show total number (should be equal to the unread number)
1588 str = g_strdup_printf("%s (%d%s%s)",
1589 name, item->total_msgs,
1590 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1591 (item->unreadmarked_msgs > 0) ? "!" : "");
1594 if (prefs_common.display_folder_unread == 1) {
1595 if (item->unread_msgs > 0) {
1596 /* show unread number and signs */
1597 str = g_strdup_printf("%s (%d%s%s)",
1598 name, item->unread_msgs,
1599 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1600 (item->unreadmarked_msgs > 0) ? "!" : "");
1603 if (item->total_msgs > 0) {
1604 /* show unread number, total number and signs if any */
1605 str = g_strdup_printf("%s (%d/%d%s%s)",
1606 name, item->unread_msgs, item->total_msgs,
1607 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1608 (item->unreadmarked_msgs > 0) ? "!" : "");
1612 if ((str == NULL) &&
1613 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1614 /* no unread/total numbers, but at least one sign */
1615 str = g_strdup_printf("%s (%s%s)",
1617 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1618 (item->unreadmarked_msgs > 0) ? "!" : "");
1622 /* last fallback, folder name only or with ! sign */
1623 str = g_strdup_printf("%s%s",
1624 name, (item->unreadmarked_msgs > 0) ? " (!)" : "");
1626 gtk_sctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1627 xpm, mask, openxpm, openmask,
1628 FALSE, GTK_CTREE_ROW(node)->expanded);
1632 if (!folder_item_parent(item)) {
1633 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1634 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1635 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1637 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1638 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1639 gtk_ctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1642 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1643 folder_has_parent_of_type(item, F_DRAFT) ||
1644 folder_has_parent_of_type(item, F_TRASH)) {
1645 use_bold = use_color = FALSE;
1646 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1647 /* highlight queue folder if there are any messages */
1648 use_bold = use_color = (item->total_msgs > 0);
1650 /* if unread messages exist, print with bold font */
1651 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1653 /* if new messages exist, print with colored letter */
1655 (item->new_msgs > 0) ||
1657 folderview_have_new_children(folderview, item));
1660 gtk_ctree_node_set_foreground(ctree, node, NULL);
1665 if (item->prefs->color > 0 && !use_color) {
1666 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1667 color_style = gtk_style_copy(bold_style);
1668 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1669 style = color_style;
1670 } else if (use_color) {
1671 style = bold_color_style;
1674 if (item->op_count > 0) {
1675 style = bold_tgtfold_style;
1677 } else if (use_color) {
1678 style = normal_color_style;
1679 gtk_ctree_node_set_foreground(ctree, node,
1680 &folderview->color_new);
1681 } else if (item->op_count > 0) {
1682 style = bold_tgtfold_style;
1683 } else if (item->prefs->color > 0) {
1685 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1686 color_style = gtk_style_copy(normal_style);
1687 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1688 style = color_style;
1690 style = normal_style;
1693 gtk_ctree_node_set_row_style(ctree, node, style);
1695 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1696 folderview_update_node(folderview, node);
1699 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1702 FolderView *folderview;
1706 g_return_if_fail(item != NULL);
1708 for (list = folderview_list; list != NULL; list = list->next) {
1709 folderview = (FolderView *)list->data;
1710 ctree = GTK_CTREE(folderview->ctree);
1712 node = gtk_ctree_find_by_row_data(ctree, NULL, item);
1714 item->search_match = matches;
1715 folderview_update_node(folderview, node);
1720 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1722 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1723 FolderView *folderview = (FolderView *)data;
1726 g_return_val_if_fail(update_info != NULL, TRUE);
1727 g_return_val_if_fail(update_info->item != NULL, TRUE);
1728 g_return_val_if_fail(folderview != NULL, FALSE);
1730 ctree = GTK_CTREE(folderview->ctree);
1732 node = gtk_ctree_find_by_row_data(ctree, NULL, update_info->item);
1735 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1736 folderview_update_node(folderview, node);
1738 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1739 update_info->item == folderview->summaryview->folder_item &&
1740 update_info->item != NULL)
1741 if (!quicksearch_is_active(folderview->summaryview->quicksearch))
1742 summary_show(folderview->summaryview, update_info->item);
1748 static gboolean folderview_gnode_func(GtkCTree *ctree, guint depth,
1749 GNode *gnode, GtkCTreeNode *cnode,
1752 FolderView *folderview = (FolderView *)data;
1753 FolderItem *item = FOLDER_ITEM(gnode->data);
1755 g_return_val_if_fail(item != NULL, FALSE);
1757 gtk_ctree_node_set_row_data(ctree, cnode, item);
1758 folderview_update_node(folderview, cnode);
1763 static void folderview_expand_func(GtkCTree *ctree, GtkCTreeNode *node,
1766 FolderView *folderview = (FolderView *)data;
1769 if (GTK_CTREE_ROW(node)->children) {
1770 item = gtk_ctree_node_get_row_data(ctree, node);
1771 g_return_if_fail(item != NULL);
1773 if (!item->collapsed)
1774 gtk_ctree_expand(ctree, node);
1776 folderview_update_node(folderview, node);
1780 static void set_special_folder(GtkCTree *ctree, FolderItem *item,
1781 GtkCTreeNode *root, GtkCTreeNode **prev)
1784 GtkCTreeNode *node, *parent, *sibling;
1786 node = gtk_ctree_find_by_row_data(ctree, root, item);
1788 g_warning("%s not found.\n", item->path);
1790 parent = GTK_CTREE_ROW(node)->parent;
1791 if (*prev && parent == GTK_CTREE_ROW(*prev)->parent)
1792 sibling = GTK_CTREE_ROW(*prev)->sibling;
1794 sibling = GTK_CTREE_ROW(parent)->children;
1798 tmp = gtk_ctree_node_get_row_data
1800 if (tmp->stype != F_NORMAL)
1801 sibling = GTK_CTREE_ROW(sibling)->sibling;
1805 if (node != sibling)
1806 gtk_ctree_move(ctree, node, parent, sibling);
1813 static void folderview_sort_folders(FolderView *folderview, GtkCTreeNode *root,
1816 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1817 GtkCTreeNode *prev = NULL;
1819 gtk_clist_freeze(GTK_CLIST(ctree));
1820 gtk_sctree_sort_recursive(ctree, root);
1821 if (root && GTK_CTREE_ROW(root)->parent) {
1822 gtk_clist_thaw(GTK_CLIST(ctree));
1825 set_special_folder(ctree, folder->inbox, root, &prev);
1826 set_special_folder(ctree, folder->outbox, root, &prev);
1827 set_special_folder(ctree, folder->draft, root, &prev);
1828 set_special_folder(ctree, folder->queue, root, &prev);
1829 set_special_folder(ctree, folder->trash, root, &prev);
1830 gtk_clist_thaw(GTK_CLIST(ctree));
1833 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1835 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
1838 g_return_if_fail(folder != NULL);
1840 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1841 folderview_gnode_func, folderview);
1842 gtk_ctree_pre_recursive(ctree, root, folderview_expand_func,
1844 folderview_sort_folders(folderview, root, folder);
1847 /* callback functions */
1848 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1849 GdkEventButton *event)
1851 GtkCList *clist = GTK_CLIST(folderview->ctree);
1854 FolderViewPopup *fpopup;
1855 GtkItemFactory *fpopup_factory;
1857 FolderItem *special_trash = NULL, *special_queue = NULL;
1861 item = gtk_clist_get_row_data(clist, row);
1863 item = folderview_get_selected_item(folderview);
1865 g_return_if_fail(item != NULL);
1866 g_return_if_fail(item->folder != NULL);
1867 folder = item->folder;
1869 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1871 fpopup_factory = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1873 fpopup = g_hash_table_lookup(folderview_popups, "common");
1874 fpopup_factory = g_hash_table_lookup(folderview->popups, "common");
1877 if (fpopup->set_sensitivity != NULL)
1878 fpopup->set_sensitivity(fpopup_factory, item);
1880 if (NULL != (ac = account_find_from_item(item))) {
1881 special_trash = account_get_special_folder(ac, F_TRASH);
1882 special_queue = account_get_special_folder(ac, F_QUEUE);
1885 if ((item == folder->trash || item == special_trash
1886 || folder_has_parent_of_type(item, F_TRASH)) &&
1887 gtk_item_factory_get_item(fpopup_factory, "/Empty trash...") == NULL) {
1888 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[0], folderview, 1);
1889 gtk_item_factory_create_item(fpopup_factory, &folder_view_trash_popup_entries[1], folderview, 1);
1890 } else if (item != folder->trash && (special_trash == NULL || item != special_trash)
1891 && !folder_has_parent_of_type(item, F_TRASH)) {
1892 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[0]);
1893 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_trash_popup_entries[1]);
1896 if ((item == folder->queue || item == special_queue
1897 || folder_has_parent_of_type(item, F_QUEUE)) &&
1898 gtk_item_factory_get_item(fpopup_factory, "/Send queue...") == NULL) {
1899 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[0], folderview, 1);
1900 gtk_item_factory_create_item(fpopup_factory, &folder_view_queue_popup_entries[1], folderview, 1);
1901 } else if (item != folder->queue && (special_queue == NULL || item != special_queue)
1902 && !folder_has_parent_of_type(item, F_QUEUE)) {
1903 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[0]);
1904 gtk_item_factory_delete_entry(fpopup_factory, &folder_view_queue_popup_entries[1]);
1907 #define SET_SENS(name, sens) \
1908 menu_set_sensitive(fpopup_factory, name, sens)
1910 SET_SENS("/Mark all read", item->unread_msgs >= 1);
1911 SET_SENS("/Search folder...", item->total_msgs >= 1 &&
1912 folderview->selected == folderview->opened);
1913 SET_SENS("/Run processing rules", item->prefs->processing &&
1914 item->total_msgs >= 1);
1915 SET_SENS("/Properties...", TRUE);
1916 SET_SENS("/Processing...", item->node->parent != NULL);
1917 if (item == folder->trash || item == special_trash
1918 || folder_has_parent_of_type(item, F_TRASH)) {
1919 GSList *msglist = folder_item_get_msg_list(item);
1920 SET_SENS("/Empty trash...", msglist != NULL);
1921 procmsg_msg_list_free(msglist);
1923 if (item == folder->queue || item == special_queue
1924 || folder_has_parent_of_type(item, F_QUEUE)) {
1925 GSList *msglist = folder_item_get_msg_list(item);
1926 SET_SENS("/Send queue...", msglist != NULL);
1927 procmsg_msg_list_free(msglist);
1931 popup = gtk_item_factory_get_widget(fpopup_factory, fpopup->path);
1932 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1933 event->button, event->time);
1938 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1939 FolderView *folderview)
1941 GtkCList *clist = GTK_CLIST(ctree);
1942 gint prev_row = -1, row = -1, column = -1;
1944 if (!event) return FALSE;
1946 if (event->button == 1 || event->button == 2) {
1947 if (!gtk_ctree_is_hot_spot (GTK_CTREE(clist), event->x, event->y))
1948 folderview->open_folder = TRUE;
1950 if (event->type == GDK_2BUTTON_PRESS) {
1951 if (clist->selection) {
1954 node = GTK_CTREE_NODE(clist->selection->data);
1956 gtk_ctree_toggle_expansion(
1959 folderview->open_folder = FALSE;
1966 if (event->button == 2 || event->button == 3) {
1968 if (clist->selection) {
1971 node = GTK_CTREE_NODE(clist->selection->data);
1973 prev_row = gtkut_ctree_get_nth_from_node
1974 (GTK_CTREE(ctree), node);
1977 if (!gtk_clist_get_selection_info(clist, event->x, event->y,
1980 if (prev_row != row) {
1981 gtk_clist_unselect_all(clist);
1982 if (event->button == 2)
1983 folderview_select_node
1985 gtk_ctree_node_nth(GTK_CTREE(ctree),
1988 gtk_clist_select_row(clist, row, column);
1992 if (event->button != 3) return FALSE;
1994 folderview_set_sens_and_popup_menu(folderview, row, event);
1998 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1999 FolderView *folderview)
2001 if (!event) return FALSE;
2003 if (event->button == 1 && folderview->open_folder == FALSE &&
2004 folderview->opened != NULL) {
2005 gtkut_ctree_set_focus_row(GTK_CTREE(ctree),
2006 folderview->opened);
2007 gtk_ctree_select(GTK_CTREE(ctree), folderview->opened);
2013 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2014 FolderView *folderview)
2016 if (!event) return FALSE;
2018 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2021 switch (event->keyval) {
2027 if (folderview->selected) {
2028 folderview_select_node(folderview,
2029 folderview->selected);
2034 if (folderview->selected && GTK_CTREE_ROW(folderview->selected)->children) {
2035 gtk_ctree_toggle_expansion(
2036 GTK_CTREE(folderview->ctree),
2037 folderview->selected);
2042 if (folderview->selected) {
2043 if (folderview->opened == folderview->selected &&
2044 (!folderview->summaryview->folder_item ||
2045 folderview->summaryview->folder_item->total_msgs == 0))
2046 folderview_select_next_unread(folderview, TRUE);
2048 folderview_select_node(folderview,
2049 folderview->selected);
2059 typedef struct _PostponedSelectData
2064 FolderView *folderview;
2065 } PostponedSelectData;
2067 static gboolean postpone_select(void *data)
2069 PostponedSelectData *psdata = (PostponedSelectData *)data;
2070 debug_print("trying again\n");
2071 psdata->folderview->open_folder = TRUE;
2072 main_window_cursor_normal(psdata->folderview->mainwin);
2073 STATUSBAR_POP(psdata->folderview->mainwin);
2074 folderview_selected(psdata->ctree, psdata->row,
2075 psdata->column, psdata->folderview);
2080 void folderview_close_opened(FolderView *folderview)
2082 if (folderview->opened) {
2083 FolderItem *olditem;
2085 olditem = gtk_ctree_node_get_row_data(GTK_CTREE(folderview->ctree),
2086 folderview->opened);
2088 gchar *buf = g_strdup_printf(_("Closing Folder %s..."),
2089 olditem->path ? olditem->path:olditem->name);
2090 /* will be null if we just moved the previously opened folder */
2091 STATUSBAR_PUSH(folderview->mainwin, buf);
2092 main_window_cursor_wait(folderview->mainwin);
2094 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2095 summary_show(folderview->summaryview, NULL);
2096 folder_item_close(olditem);
2097 main_window_cursor_normal(folderview->mainwin);
2098 STATUSBAR_POP(folderview->mainwin);
2102 if (folderview->opened &&
2103 !GTK_CTREE_ROW(folderview->opened)->children)
2104 gtk_ctree_collapse(GTK_CTREE(folderview->ctree), folderview->opened);
2106 folderview->opened = NULL;
2108 static void folderview_selected(GtkCTree *ctree, GtkCTreeNode *row,
2109 gint column, FolderView *folderview)
2111 static gboolean can_select = TRUE; /* exclusive lock */
2116 GtkCTreeNode *old_opened = folderview->opened;
2118 folderview->selected = row;
2120 debug_print("newly selected %p, opened %p\n", folderview->selected,
2121 folderview->opened);
2122 if (folderview->opened == row) {
2123 folderview->open_folder = FALSE;
2128 if (!can_select || summary_is_locked(folderview->summaryview)) {
2129 if (folderview->opened) {
2130 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2131 gtk_ctree_select(ctree, folderview->opened);
2133 folderview->open_folder = FALSE;
2138 if (!folderview->open_folder) {
2142 item = gtk_ctree_node_get_row_data(ctree, row);
2143 if (!item || item->no_select) {
2145 folderview->open_folder = FALSE;
2151 /* Save cache for old folder */
2152 /* We don't want to lose all caches if sylpheed crashed */
2153 /* resets folderview->opened to NULL */
2154 folderview_close_opened(folderview);
2156 /* CLAWS: set compose button type: news folder items
2157 * always have a news folder as parent */
2159 toolbar_set_compose_button
2160 (folderview->mainwin->toolbar,
2161 FOLDER_TYPE(item->folder) == F_NEWS ?
2162 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2165 debug_print("Folder %s is selected\n", item->path);
2167 if (!GTK_CTREE_ROW(row)->children)
2168 gtk_ctree_expand(ctree, row);
2170 /* ungrab the mouse event */
2171 if (GTK_WIDGET_HAS_GRAB(ctree)) {
2172 gtk_grab_remove(GTK_WIDGET(ctree));
2173 if (gdk_pointer_is_grabbed())
2174 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2178 buf = g_strdup_printf(_("Opening Folder %s..."), item->path ?
2179 item->path : "(null)");
2180 debug_print("%s\n", buf);
2181 STATUSBAR_PUSH(folderview->mainwin, buf);
2184 main_window_cursor_wait(folderview->mainwin);
2186 res = folder_item_open(item);
2188 main_window_cursor_normal(folderview->mainwin);
2189 STATUSBAR_POP(folderview->mainwin);
2191 alertpanel_error(_("Folder could not be opened."));
2193 folderview->open_folder = FALSE;
2197 } else if (res == -2) {
2198 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2199 data->ctree = ctree;
2201 data->column = column;
2202 data->folderview = folderview;
2203 debug_print("postponing open of %s till end of scan\n",
2204 item->path ? item->path:item->name);
2205 folderview->open_folder = FALSE;
2207 g_timeout_add(500, postpone_select, data);
2212 main_window_cursor_normal(folderview->mainwin);
2215 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2216 opened = summary_show(folderview->summaryview, item);
2218 folder_clean_cache_memory(item);
2221 gtkut_ctree_set_focus_row(ctree, old_opened);
2222 gtk_ctree_select(ctree, old_opened);
2223 folderview->opened = old_opened;
2225 folderview->opened = row;
2226 if (gtk_ctree_node_is_visible(ctree, row)
2227 != GTK_VISIBILITY_FULL)
2228 gtk_ctree_node_moveto(ctree, row, -1, 0.5, 0);
2231 STATUSBAR_POP(folderview->mainwin);
2233 folderview->open_folder = FALSE;
2238 static void folderview_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
2239 FolderView *folderview)
2243 item = gtk_ctree_node_get_row_data(ctree, node);
2244 g_return_if_fail(item != NULL);
2245 item->collapsed = FALSE;
2246 folderview_update_node(folderview, node);
2249 static void folderview_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
2250 FolderView *folderview)
2254 item = gtk_ctree_node_get_row_data(ctree, node);
2255 g_return_if_fail(item != NULL);
2256 item->collapsed = TRUE;
2257 folderview_update_node(folderview, node);
2260 static void folderview_popup_close(GtkMenuShell *menu_shell,
2261 FolderView *folderview)
2263 if (!folderview->opened) return;
2265 gtk_ctree_select(GTK_CTREE(folderview->ctree), folderview->opened);
2268 static void folderview_col_resized(GtkCList *clist, gint column, gint width,
2269 FolderView *folderview)
2271 FolderColumnType type = folderview->col_state[column].type;
2273 prefs_common.folder_col_size[type] = width;
2276 static void folderview_create_folder_node_recursive(FolderView *folderview, FolderItem *item)
2280 folderview_create_folder_node(folderview, item);
2282 if (!item || !item->folder || !item->folder->node)
2285 srcnode = item->folder->node;
2286 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, item);
2287 srcnode = srcnode->children;
2288 while (srcnode != NULL) {
2289 if (srcnode && srcnode->data) {
2290 FolderItem *next_item = (FolderItem*) srcnode->data;
2291 folderview_create_folder_node_recursive(folderview, next_item);
2293 srcnode = srcnode->next;
2297 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2299 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2300 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2301 GtkCTreeNode *node, *parent_node;
2302 gint *col_pos = folderview->col_pos;
2303 FolderItemUpdateData hookdata;
2305 parent_node = gtk_ctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2306 if (parent_node == NULL)
2309 gtk_clist_freeze(GTK_CLIST(ctree));
2311 text[col_pos[F_COL_FOLDER]] = item->name;
2312 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2314 folderxpm, folderxpmmask,
2315 folderopenxpm, folderopenxpmmask,
2317 gtk_ctree_expand(ctree, parent_node);
2318 gtk_ctree_node_set_row_data(ctree, node, item);
2320 gtk_ctree_node_set_row_style(ctree, node, normal_style);
2321 folderview_sort_folders(folderview, parent_node, item->folder);
2323 hookdata.item = item;
2324 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2325 hookdata.msg = NULL;
2326 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2328 gtk_clist_thaw(GTK_CLIST(ctree));
2331 static void folderview_empty_trash_cb(FolderView *folderview, guint action,
2334 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2336 GSList *mlist = NULL;
2338 FolderItem *special_trash = NULL;
2341 if (!folderview->selected) return;
2342 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2343 g_return_if_fail(item != NULL);
2344 g_return_if_fail(item->folder != NULL);
2346 if (NULL != (ac = account_find_from_item(item)))
2347 special_trash = account_get_special_folder(ac, F_TRASH);
2349 if (item != item->folder->trash && item != special_trash
2350 && !folder_has_parent_of_type(item, F_TRASH)) return;
2352 if (prefs_common.ask_on_clean) {
2353 if (alertpanel(_("Empty trash"),
2354 _("Delete all messages in trash?"),
2355 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2359 mlist = folder_item_get_msg_list(item);
2361 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2362 MsgInfo * msginfo = (MsgInfo *) cur->data;
2363 if (MSG_IS_LOCKED(msginfo->flags))
2365 /* is it partially received? (partial_recv isn't cached) */
2366 if (msginfo->total_size != 0 &&
2367 msginfo->size != (off_t)msginfo->total_size)
2368 partial_mark_for_delete(msginfo);
2370 procmsg_msg_list_free(mlist);
2372 folder_item_remove_all_msg(item);
2375 static void folderview_send_queue_cb(FolderView *folderview, guint action,
2378 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2380 FolderItem *special_queue = NULL;
2382 gchar *errstr = NULL;
2384 if (!folderview->selected) return;
2385 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2386 g_return_if_fail(item != NULL);
2387 g_return_if_fail(item->folder != NULL);
2389 if (NULL != (ac = account_find_from_item(item)))
2390 special_queue = account_get_special_folder(ac, F_QUEUE);
2392 if (item != item->folder->queue && item != special_queue
2393 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2395 if (procmsg_queue_is_empty(item))
2398 if (prefs_common.work_offline)
2399 if (alertpanel(_("Offline warning"),
2400 _("You're working offline. Override?"),
2401 GTK_STOCK_NO, GTK_STOCK_YES,
2402 NULL) != G_ALERTALTERNATE)
2405 /* ask for confirmation before sending queued messages only
2406 in online mode and if there is at least one message queued
2407 in any of the folder queue
2409 if (prefs_common.confirm_send_queued_messages) {
2410 if (!prefs_common.work_offline) {
2411 if (alertpanel(_("Send queued messages"),
2412 _("Send all queued messages?"),
2413 GTK_STOCK_CANCEL, _("_Send"),
2414 NULL) != G_ALERTALTERNATE)
2419 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2421 alertpanel_error_log(_("Some errors occurred while "
2422 "sending queued messages."));
2424 gchar *tmp = g_strdup_printf(_("Some errors occurred "
2425 "while sending queued messages:\n%s"), errstr);
2427 alertpanel_error_log(tmp);
2433 static void folderview_search_cb(FolderView *folderview, guint action,
2436 summary_search(folderview->summaryview);
2439 static void folderview_run_processing_cb(FolderView *folderview, guint action,
2442 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2445 if (!folderview->selected) return;
2447 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2448 g_return_if_fail(item != NULL);
2449 g_return_if_fail(item->folder != NULL);
2451 folder_item_apply_processing(item);
2454 static void folderview_property_cb(FolderView *folderview, guint action,
2457 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2460 if (!folderview->selected) return;
2462 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2463 g_return_if_fail(item != NULL);
2464 g_return_if_fail(item->folder != NULL);
2466 prefs_folder_item_open(item);
2469 static void folderview_recollapse_nodes(FolderView *folderview, GtkCTreeNode *node)
2471 GSList *list = NULL;
2472 GSList *done = NULL;
2473 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2475 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2476 if (!gtkut_ctree_node_is_parent(GTK_CTREE_NODE(list->data), node)
2477 && list->data != node) {
2478 gtk_ctree_collapse(ctree, GTK_CTREE_NODE(list->data));
2479 done = g_slist_append(done, GTK_CTREE_NODE(list->data));
2482 for (list = done; list != NULL; list = g_slist_next(list)) {
2483 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2489 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2490 FolderItem *to_folder, gboolean copy)
2492 FolderItem *from_parent = NULL;
2493 FolderItem *new_folder = NULL;
2494 GtkCTreeNode *src_node = NULL;
2498 g_return_if_fail(folderview != NULL);
2499 g_return_if_fail(from_folder != NULL);
2500 g_return_if_fail(to_folder != NULL);
2502 src_node = gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree), NULL, from_folder);
2503 from_parent = folder_item_parent(from_folder);
2505 if (prefs_common.warn_dnd) {
2506 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s' ?"):
2507 _("Do you really want to make folder '%s' a sub-folder of '%s' ?"),
2508 from_folder->name, to_folder->name);
2509 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2510 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2511 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2514 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2516 else if (status & G_ALERTDISABLE)
2517 prefs_common.warn_dnd = FALSE;
2520 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2521 from_folder->name, to_folder->name);
2522 STATUSBAR_PUSH(folderview->mainwin, buf);
2524 summary_clear_all(folderview->summaryview);
2525 folderview->opened = NULL;
2526 folderview->selected = NULL;
2527 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2529 main_window_cursor_wait(folderview->mainwin);
2531 statusbar_verbosity_set(FALSE);
2532 folder_item_update_freeze();
2533 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2534 statusbar_verbosity_set(FALSE);
2535 main_window_cursor_normal(folderview->mainwin);
2536 STATUSBAR_POP(folderview->mainwin);
2537 folder_item_update_thaw();
2538 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2540 folderview_sort_folders(folderview,
2541 gtk_ctree_find_by_row_data(GTK_CTREE(folderview->ctree),
2542 NULL, to_folder), new_folder->folder);
2543 folderview_select(folderview, new_folder);
2545 statusbar_verbosity_set(FALSE);
2546 main_window_cursor_normal(folderview->mainwin);
2547 STATUSBAR_POP(folderview->mainwin);
2548 folder_item_update_thaw();
2550 case F_MOVE_FAILED_DEST_IS_PARENT:
2551 alertpanel_error(_("Source and destination are the same."));
2553 case F_MOVE_FAILED_DEST_IS_CHILD:
2554 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2555 _("Can't move a folder to one of its children."));
2557 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2558 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2561 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2566 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2569 static gint folderview_clist_compare(GtkCList *clist,
2570 gconstpointer ptr1, gconstpointer ptr2)
2572 FolderItem *item1 = ((GtkCListRow *)ptr1)->data;
2573 FolderItem *item2 = ((GtkCListRow *)ptr2)->data;
2575 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2577 return item1->order - item2->order;
2580 // if only one folder has an order it comes first
2581 if (item1->order > 0)
2585 if (item2->order > 0)
2591 return (item2->name != NULL);
2595 return g_utf8_collate(item1->name, item2->name);
2598 static void folderview_processing_cb(FolderView *folderview, guint action,
2601 GtkCTree *ctree = GTK_CTREE(folderview->ctree);
2605 if (!folderview->selected) return;
2607 item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
2608 g_return_if_fail(item != NULL);
2609 g_return_if_fail(item->folder != NULL);
2611 id = folder_item_get_identifier(item);
2612 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2615 prefs_filtering_open(&item->prefs->processing, title,
2616 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2620 void folderview_set_target_folder_color(gint color_op)
2624 FolderView *folderview;
2626 for (list = folderview_list; list != NULL; list = list->next) {
2627 folderview = (FolderView *)list->data;
2628 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2630 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2631 folderview->color_op;
2637 static gchar *last_font = NULL;
2638 void folderview_reflect_prefs_pixmap_theme(FolderView *folderview)
2646 void folderview_reflect_prefs(void)
2648 gboolean update_font = TRUE;
2649 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2650 FolderItem *item = folderview_get_selected_item(folderview);
2651 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2652 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2653 gint height = pos->value;
2655 if (last_font && !strcmp(last_font, NORMAL_FONT))
2656 update_font = FALSE;
2659 last_font = g_strdup(NORMAL_FONT);
2662 normal_style = normal_color_style = bold_style =
2663 bold_color_style = bold_tgtfold_style = NULL;
2665 folderview_init(folderview);
2667 gtk_clist_freeze(GTK_CLIST(folderview->ctree));
2668 folderview_column_set_titles(folderview);
2669 folderview_set_all();
2671 g_signal_handlers_block_by_func
2672 (G_OBJECT(folderview->ctree),
2673 G_CALLBACK(folderview_selected), folderview);
2676 GtkCTreeNode *node = gtk_ctree_find_by_row_data(
2677 GTK_CTREE(folderview->ctree), NULL, item);
2679 folderview_select(folderview, item);
2680 folderview->open_folder = FALSE;
2681 folderview->selected = node;
2684 g_signal_handlers_unblock_by_func
2685 (G_OBJECT(folderview->ctree),
2686 G_CALLBACK(folderview_selected), folderview);
2688 pos = gtk_scrolled_window_get_vadjustment(
2689 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2690 gtk_adjustment_set_value(pos, height);
2691 gtk_clist_thaw(GTK_CLIST(folderview->ctree));
2694 static void drag_state_stop(FolderView *folderview)
2696 if (folderview->drag_timer)
2697 g_source_remove(folderview->drag_timer);
2698 folderview->drag_timer = 0;
2699 folderview->drag_node = NULL;
2702 static gint folderview_defer_expand(FolderView *folderview)
2704 if (folderview->drag_node) {
2705 folderview_recollapse_nodes(folderview, folderview->drag_node);
2706 if (folderview->drag_item->collapsed) {
2707 gtk_ctree_expand(GTK_CTREE(folderview->ctree), folderview->drag_node);
2708 folderview->nodes_to_recollapse = g_slist_append
2709 (folderview->nodes_to_recollapse, folderview->drag_node);
2712 folderview->drag_item = NULL;
2713 folderview->drag_timer = 0;
2717 static void drag_state_start(FolderView *folderview, GtkCTreeNode *node, FolderItem *item)
2719 /* the idea is that we call drag_state_start() whenever we want expansion to
2720 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2721 * we need to call drag_state_stop() */
2722 drag_state_stop(folderview);
2723 /* request expansion */
2724 if (0 != (folderview->drag_timer = g_timeout_add
2725 (prefs_common.hover_timeout,
2726 (GtkFunction)folderview_defer_expand,
2728 folderview->drag_node = node;
2729 folderview->drag_item = item;
2733 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2734 FolderView *folderview)
2736 GdkDragContext *context;
2738 g_return_if_fail(folderview != NULL);
2739 if (folderview->selected == NULL) return;
2740 if (folderview->nodes_to_recollapse)
2741 g_slist_free(folderview->nodes_to_recollapse);
2742 folderview->nodes_to_recollapse = NULL;
2743 context = gtk_drag_begin(widget, folderview->target_list,
2744 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2745 gtk_drag_set_icon_default(context);
2748 static void folderview_drag_data_get(GtkWidget *widget,
2749 GdkDragContext *drag_context,
2750 GtkSelectionData *selection_data,
2753 FolderView *folderview)
2757 gchar *source = NULL;
2758 if (info == TARGET_DUMMY) {
2759 for (cur = GTK_CLIST(folderview->ctree)->selection;
2760 cur != NULL; cur = cur->next) {
2761 item = gtk_ctree_node_get_row_data
2762 (GTK_CTREE(folderview->ctree),
2763 GTK_CTREE_NODE(cur->data));
2765 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2766 gtk_selection_data_set(selection_data,
2767 selection_data->target, 8,
2768 source, strlen(source));
2774 g_warning("unknown info %d\n", info);
2778 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2780 FolderUpdateData *hookdata;
2781 FolderView *folderview;
2785 folderview = (FolderView *) userdata;
2786 g_return_val_if_fail(hookdata != NULL, FALSE);
2787 g_return_val_if_fail(folderview != NULL, FALSE);
2789 ctree = folderview->ctree;
2790 g_return_val_if_fail(ctree != NULL, FALSE);
2792 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2793 folderview_create_folder_node(folderview, hookdata->item);
2794 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2795 GtkCTreeNode *node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree),
2796 NULL, folder_item_parent(hookdata->item));
2797 folderview_sort_folders(folderview, node, hookdata->folder);
2798 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2801 node = gtk_ctree_find_by_row_data(GTK_CTREE(ctree), NULL, hookdata->item);
2803 gtk_ctree_remove_node(GTK_CTREE(ctree), node);
2804 if (folderview->selected == node)
2805 folderview->selected = NULL;
2806 if (folderview->opened == node)
2807 folderview->opened = NULL;
2809 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2810 folderview_set(folderview);
2815 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2816 GdkDragContext *context,
2820 FolderView *folderview)
2823 FolderItem *item = NULL, *src_item = NULL;
2824 GtkCTreeNode *node = NULL;
2825 gboolean acceptable = FALSE;
2826 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2827 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2828 int height = (int)pos->page_size;
2829 int total_height = (int)pos->upper;
2830 int vpos = (int) pos->value;
2832 if (gtk_clist_get_selection_info
2833 (GTK_CLIST(widget), x - 24, y - 24, &row, &column)) {
2834 GtkWidget *srcwidget;
2836 if (y > height - 24 && height + vpos < total_height)
2837 gtk_adjustment_set_value(pos, (vpos+5 > height ? height : vpos+5));
2839 if (y < 48 && y > 0)
2840 gtk_adjustment_set_value(pos, (vpos-5 < 0 ? 0 : vpos-5));
2842 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2843 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2844 src_item = folderview->summaryview->folder_item;
2846 srcwidget = gtk_drag_get_source_widget(context);
2847 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2848 /* comes from summaryview */
2849 /* we are copying messages, so only accept folder items that are not
2850 the source item, are no root items and can copy messages */
2851 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2852 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2853 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2855 } else if (srcwidget == folderview->ctree) {
2856 /* comes from folderview */
2857 /* we are moving folder items, only accept folders that are not
2858 the source items and can copy messages and create folder items */
2859 if (item && item->folder && src_item && src_item != item &&
2860 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2861 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2862 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2863 || item->folder == src_item->folder))
2866 /* comes from another app */
2867 /* we are adding messages, so only accept folder items that are
2868 no root items and can copy messages */
2869 if (item && item->folder && folder_item_parent(item) != NULL
2870 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2871 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2876 if (acceptable || (src_item && src_item == item))
2877 drag_state_start(folderview, node, item);
2880 g_signal_handlers_block_by_func
2882 G_CALLBACK(folderview_selected), folderview);
2883 gtk_ctree_select(GTK_CTREE(widget), node);
2884 g_signal_handlers_unblock_by_func
2886 G_CALLBACK(folderview_selected), folderview);
2887 gdk_drag_status(context,
2888 (context->actions == GDK_ACTION_COPY ?
2889 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2891 if (folderview->opened)
2892 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2893 gdk_drag_status(context, 0, time);
2899 static void folderview_drag_leave_cb(GtkWidget *widget,
2900 GdkDragContext *context,
2902 FolderView *folderview)
2904 drag_state_stop(folderview);
2905 gtk_ctree_select(GTK_CTREE(widget), folderview->opened);
2908 static void free_info (gpointer stuff, gpointer data)
2913 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2914 guint time, FolderItem *item)
2917 GSList *msglist = NULL;
2918 list = uri_list_extract_filenames(data);
2919 if (!(item && item->folder && folder_item_parent(item) != NULL
2920 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2922 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2923 debug_print("item doesn't fit\n");
2927 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2928 debug_print("list is empty\n");
2931 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2932 MsgFileInfo *info = NULL;
2934 if (file_is_email((gchar *)tmp->data)) {
2935 info = g_new0(MsgFileInfo, 1);
2936 info->msginfo = NULL;
2937 info->file = (gchar *)tmp->data;
2938 msglist = g_slist_prepend(msglist, info);
2939 debug_print("file is a mail\n");
2941 debug_print("file isn't a mail\n");
2945 msglist = g_slist_reverse(msglist);
2946 folder_item_add_msgs(item, msglist, FALSE);
2947 g_slist_foreach(msglist, free_info, NULL);
2948 g_slist_free(msglist);
2949 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2951 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2953 list_free_strings(list);
2957 static void folderview_drag_received_cb(GtkWidget *widget,
2958 GdkDragContext *drag_context,
2961 GtkSelectionData *data,
2964 FolderView *folderview)
2967 FolderItem *item = NULL, *src_item;
2970 if (info == TARGET_DUMMY) {
2971 drag_state_stop(folderview);
2972 if ((void *)strstr(data->data, "FROM_OTHER_FOLDER") != (void *)data->data) {
2973 /* comes from summaryview */
2974 if (gtk_clist_get_selection_info
2975 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
2978 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
2979 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
2980 src_item = folderview->summaryview->folder_item;
2982 /* re-check (due to acceptable possibly set for folder moves */
2983 if (!(item && item->folder && item->path && !item->no_select &&
2984 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2987 if (item && src_item) {
2988 switch (drag_context->action) {
2989 case GDK_ACTION_COPY:
2990 summary_copy_selected_to(folderview->summaryview, item);
2991 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2993 case GDK_ACTION_MOVE:
2994 case GDK_ACTION_DEFAULT:
2996 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
2997 summary_copy_selected_to(folderview->summaryview, item);
2999 summary_move_selected_to(folderview->summaryview, item);
3000 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3003 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3005 /* comes from folderview */
3007 gboolean folder_is_normal = TRUE;
3008 gboolean copy = (drag_context->action == GDK_ACTION_COPY);
3010 source = data->data + 17;
3011 if (gtk_clist_get_selection_info
3012 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0
3014 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3017 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3018 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3019 src_item = folder_find_item_from_identifier(source);
3023 src_item->stype == F_NORMAL &&
3024 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3025 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3026 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3027 !folder_has_parent_of_type(src_item, F_TRASH);
3028 if (!item || item->no_select || !src_item
3029 || !folder_is_normal) {
3030 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3034 folderview_move_folder(folderview, src_item, item, copy);
3035 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3037 folderview->nodes_to_recollapse = NULL;
3038 } else if (info == TARGET_MAIL_URI_LIST) {
3039 if (gtk_clist_get_selection_info
3040 (GTK_CLIST(widget), x - 24, y - 24, &row, &column) == 0)
3043 node = gtk_ctree_node_nth(GTK_CTREE(widget), row);
3045 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3046 debug_print("no node\n");
3049 item = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node);
3051 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3052 debug_print("no item\n");
3055 folderview_finish_dnd(data->data, drag_context, time, item);
3059 static void folderview_drag_end_cb(GtkWidget *widget,
3060 GdkDragContext *drag_context,
3061 FolderView *folderview)
3063 drag_state_stop(folderview);
3064 g_slist_free(folderview->nodes_to_recollapse);
3065 folderview->nodes_to_recollapse = NULL;
3068 void folderview_register_popup(FolderViewPopup *fpopup)
3072 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3073 FolderView *folderview = folderviews->data;
3074 GtkItemFactory *factory;
3076 factory = create_ifactory(folderview, fpopup);
3077 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3079 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3082 void folderview_unregister_popup(FolderViewPopup *fpopup)
3086 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3087 FolderView *folderview = folderviews->data;
3089 g_hash_table_remove(folderview->popups, fpopup->klass);
3091 g_hash_table_remove(folderview_popups, fpopup->klass);