2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 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>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
61 #include "gtkcmctree.h"
63 #define COL_FOLDER_WIDTH 150
64 #define COL_NUM_WIDTH 32
66 static GList *folderview_list = NULL;
68 static GtkStyle *normal_style;
69 static GtkStyle *normal_color_style;
70 static GtkStyle *bold_style;
71 static GtkStyle *bold_color_style;
72 static GtkStyle *bold_tgtfold_style;
74 static GdkPixbuf *inboxxpm;
75 static GdkPixbuf *inboxhrmxpm;
76 static GdkPixbuf *inboxopenxpm;
77 static GdkPixbuf *inboxopenhrmxpm;
78 static GdkPixbuf *outboxxpm;
79 static GdkPixbuf *outboxhrmxpm;
80 static GdkPixbuf *outboxopenxpm;
81 static GdkPixbuf *outboxopenhrmxpm;
82 static GdkPixbuf *folderxpm;
83 static GdkPixbuf *folderhrmxpm;
84 static GdkPixbuf *folderopenxpm;
85 static GdkPixbuf *folderopenhrmxpm;
86 static GdkPixbuf *trashopenxpm;
87 static GdkPixbuf *trashopenhrmxpm;
88 static GdkPixbuf *trashxpm;
89 static GdkPixbuf *trashhrmxpm;
90 static GdkPixbuf *queuexpm;
91 static GdkPixbuf *queuehrmxpm;
92 static GdkPixbuf *queueopenxpm;
93 static GdkPixbuf *queueopenhrmxpm;
94 static GdkPixbuf *draftsxpm;
95 static GdkPixbuf *draftsopenxpm;
96 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *foldersubsxpm;
98 static GdkPixbuf *foldersubsopenxpm;
100 static GdkPixbuf *m_inboxxpm;
101 static GdkPixbuf *m_inboxhrmxpm;
102 static GdkPixbuf *m_inboxopenxpm;
103 static GdkPixbuf *m_inboxopenhrmxpm;
104 static GdkPixbuf *m_outboxxpm;
105 static GdkPixbuf *m_outboxhrmxpm;
106 static GdkPixbuf *m_outboxopenxpm;
107 static GdkPixbuf *m_outboxopenhrmxpm;
108 static GdkPixbuf *m_folderxpm;
109 static GdkPixbuf *m_folderhrmxpm;
110 static GdkPixbuf *m_folderopenxpm;
111 static GdkPixbuf *m_folderopenhrmxpm;
112 static GdkPixbuf *m_trashopenxpm;
113 static GdkPixbuf *m_trashopenhrmxpm;
114 static GdkPixbuf *m_trashxpm;
115 static GdkPixbuf *m_trashhrmxpm;
116 static GdkPixbuf *m_queuexpm;
117 static GdkPixbuf *m_queuehrmxpm;
118 static GdkPixbuf *m_queueopenxpm;
119 static GdkPixbuf *m_queueopenhrmxpm;
120 static GdkPixbuf *m_draftsxpm;
121 static GdkPixbuf *m_draftsopenxpm;
122 static GdkPixbuf *m_foldersubsxpm;
123 static GdkPixbuf *m_foldersubsopenxpm;
125 static GdkPixbuf *newxpm;
126 static GdkPixbuf *unreadxpm;
127 static GdkPixbuf *readxpm;
129 static void folderview_select_node (FolderView *folderview,
130 GtkCMCTreeNode *node);
131 static void folderview_set_folders (FolderView *folderview);
132 static void folderview_sort_folders (FolderView *folderview,
133 GtkCMCTreeNode *root,
135 static void folderview_append_folder (FolderView *folderview,
137 static void folderview_update_node (FolderView *folderview,
138 GtkCMCTreeNode *node);
140 static gint folderview_clist_compare (GtkCMCList *clist,
144 /* callback functions */
145 static gboolean folderview_button_pressed (GtkWidget *ctree,
146 GdkEventButton *event,
147 FolderView *folderview);
148 static gboolean folderview_button_released (GtkWidget *ctree,
149 GdkEventButton *event,
150 FolderView *folderview);
151 static gboolean folderview_key_pressed (GtkWidget *widget,
153 FolderView *folderview);
154 static void folderview_selected (GtkCMCTree *ctree,
157 FolderView *folderview);
158 static void folderview_tree_expanded (GtkCMCTree *ctree,
159 GtkCMCTreeNode *node,
160 FolderView *folderview);
161 static void folderview_tree_collapsed (GtkCMCTree *ctree,
162 GtkCMCTreeNode *node,
163 FolderView *folderview);
164 static void folderview_popup_close (GtkMenuShell *menu_shell,
165 FolderView *folderview);
166 static void folderview_col_resized (GtkCMCList *clist,
169 FolderView *folderview);
171 static void mark_all_read_handler (GtkAction *action,
175 static void mark_all_read_cb (GtkAction *action,
177 static void mark_all_read_recursive_cb (GtkAction *action,
180 static void folderview_empty_trash_cb (GtkAction *action,
183 static void folderview_send_queue_cb (GtkAction *action,
186 static void folderview_search_cb (GtkAction *action,
188 static void folderview_run_processing_cb(GtkAction *action,
191 static void folderview_property_cb (GtkAction *action,
194 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
195 GdkDragContext *context,
199 FolderView *folderview);
200 static void folderview_drag_leave_cb (GtkWidget *widget,
201 GdkDragContext *context,
203 FolderView *folderview);
204 static void folderview_drag_received_cb (GtkWidget *widget,
205 GdkDragContext *drag_context,
208 GtkSelectionData *data,
211 FolderView *folderview);
213 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
214 FolderView *folderview);
216 static void folderview_drag_data_get (GtkWidget *widget,
217 GdkDragContext *drag_context,
218 GtkSelectionData *selection_data,
221 FolderView *folderview);
222 static void folderview_drag_end_cb (GtkWidget *widget,
223 GdkDragContext *drag_context,
224 FolderView *folderview);
226 static void folderview_create_folder_node (FolderView *folderview,
228 static gboolean folderview_update_folder (gpointer source,
230 static gboolean folderview_update_item_claws (gpointer source,
232 static void folderview_processing_cb(GtkAction *action, gpointer data);
233 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
234 GdkEventButton *event);
236 GHashTable *folderview_popups;
238 static GtkActionEntry folderview_common_popup_entries[] =
240 {"FolderViewPopup", NULL, "FolderViewPopup" },
241 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
242 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
243 {"FolderViewPopup/---", NULL, "---" },
244 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
245 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
246 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
247 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
248 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
249 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
253 GtkTargetEntry folderview_drag_types[] =
255 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
256 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
259 void folderview_initialize(void)
261 FolderViewPopup *fpopup;
263 fpopup = g_new0(FolderViewPopup, 1);
265 fpopup->klass = "common";
266 fpopup->path = "<CommonFolder>";
267 fpopup->entries = folderview_common_popup_entries;
268 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
269 fpopup->set_sensitivity = NULL;
271 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
272 g_hash_table_insert(folderview_popups, "common", fpopup);
275 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
277 FolderViewPopup *fpopup_common;
278 GtkActionGroup *action_group;
280 action_group = cm_menu_create_action_group(
282 fpopup->entries, fpopup->n_entries,
283 (gpointer)folderview);
285 if (fpopup->toggle_entries)
286 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
287 fpopup->n_toggle_entries,
288 (gpointer)folderview);
289 if (fpopup->radio_entries)
290 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
291 fpopup->n_radio_entries, fpopup->radio_default,
292 G_CALLBACK(fpopup->radio_callback),
293 (gpointer)folderview);
295 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
296 if (fpopup_common != fpopup) {
297 gtk_action_group_add_actions(action_group, fpopup_common->entries,
298 fpopup_common->n_entries,
299 (gpointer)folderview);
300 if (fpopup_common->toggle_entries)
301 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
302 fpopup_common->n_toggle_entries,
303 (gpointer)folderview);
304 if (fpopup_common->radio_entries)
305 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
306 fpopup_common->n_radio_entries, fpopup_common->radio_default,
307 G_CALLBACK(fpopup_common->radio_callback),
308 (gpointer)folderview);
314 static void create_action_groups(gpointer key, gpointer value, gpointer data)
316 FolderView *folderview = data;
317 FolderViewPopup *fpopup = value;
318 GtkActionGroup *group;
320 group = create_action_group(folderview, fpopup);
321 g_hash_table_insert(folderview->popups, fpopup->klass, group);
324 static void folderview_column_set_titles(FolderView *folderview)
326 GtkWidget *ctree = folderview->ctree;
327 GtkWidget *label_folder;
328 GtkWidget *label_new;
329 GtkWidget *label_unread;
330 GtkWidget *label_total;
331 GtkWidget *hbox_folder;
333 GtkWidget *hbox_unread;
334 GtkWidget *hbox_total;
335 gint *col_pos = folderview->col_pos;
337 debug_print("setting titles...\n");
338 gtk_widget_realize(folderview->ctree);
339 gtk_widget_show_all(folderview->scrolledwin);
341 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
342 * instead text (text overflows making them unreadable and ugly) */
343 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
344 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
345 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
347 label_folder = gtk_label_new(_("Folder"));
348 label_new = gtk_image_new_from_pixbuf(newxpm);
349 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
350 label_total = gtk_image_new_from_pixbuf(readxpm);
352 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
354 hbox_folder = gtk_hbox_new(FALSE, 4);
355 hbox_new = gtk_hbox_new(FALSE, 4);
356 hbox_unread = gtk_hbox_new(FALSE, 4);
357 hbox_total = gtk_hbox_new(FALSE, 4);
360 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
361 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
362 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
363 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
364 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
365 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
366 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
367 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
369 gtk_widget_show_all(hbox_folder);
370 gtk_widget_show_all(hbox_new);
371 gtk_widget_show_all(hbox_unread);
372 gtk_widget_show_all(hbox_total);
375 gtk_widget_set_size_request(hbox_new, -1, 20);
376 gtk_widget_set_size_request(hbox_unread, -1, 20);
377 gtk_widget_set_size_request(hbox_total, -1, 20);
380 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
381 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
382 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
383 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
389 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
390 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
391 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
394 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
396 FolderView *folderview = (FolderView *)data;
397 GdkEventButton event;
398 if (folderview_get_selected_item(folderview) == NULL)
402 event.time = gtk_get_current_event_time();
404 folderview_set_sens_and_popup_menu(folderview, -1,
411 static GtkWidget *folderview_ctree_create(FolderView *folderview)
415 FolderColumnState *col_state;
416 FolderColumnType type;
417 gchar *titles[N_FOLDER_COLS];
419 GtkWidget *scrolledwin = folderview->scrolledwin;
421 debug_print("creating tree...\n");
422 memset(titles, 0, sizeof(titles));
424 col_state = prefs_folder_column_get_config();
425 memset(titles, 0, sizeof(titles));
427 col_pos = folderview->col_pos;
429 for (i = 0; i < N_FOLDER_COLS; i++) {
430 folderview->col_state[i] = col_state[i];
431 type = col_state[i].type;
435 titles[col_pos[F_COL_FOLDER]] = _("Folder");
436 titles[col_pos[F_COL_NEW]] = _("New");
437 titles[col_pos[F_COL_UNREAD]] = _("Unread");
438 /* TRANSLATORS: This in Number sign in American style */
439 titles[col_pos[F_COL_TOTAL]] = _("#");
441 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
444 if (prefs_common.show_col_headers == FALSE)
445 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
448 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
449 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
451 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
452 col_pos[F_COL_UNREAD],
454 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
455 col_pos[F_COL_TOTAL],
457 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
458 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
459 GTK_CMCTREE_EXPANDER_TRIANGLE);
461 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
462 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
464 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
465 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
467 /* don't let title buttons take key focus */
468 for (i = 0; i < N_FOLDER_COLS; i++) {
469 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
470 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
471 prefs_common.folder_col_size[i]);
472 gtk_cmclist_set_column_visibility
473 (GTK_CMCLIST(ctree), i, col_state[i].visible);
476 g_signal_connect(G_OBJECT(ctree), "key_press_event",
477 G_CALLBACK(folderview_key_pressed),
479 g_signal_connect(G_OBJECT(ctree), "button_press_event",
480 G_CALLBACK(folderview_button_pressed),
482 g_signal_connect(G_OBJECT(ctree), "popup-menu",
483 G_CALLBACK(folderview_popup_menu), folderview);
484 g_signal_connect(G_OBJECT(ctree), "button_release_event",
485 G_CALLBACK(folderview_button_released),
487 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
488 G_CALLBACK(folderview_selected), folderview);
490 /* drag-n-dropping folders on maemo is impractical as this
491 * opens the folder almost everytime */
492 g_signal_connect(G_OBJECT(ctree), "start_drag",
493 G_CALLBACK(folderview_start_drag), folderview);
495 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
496 G_CALLBACK(folderview_drag_data_get),
499 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
500 G_CALLBACK(folderview_tree_expanded),
502 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
503 G_CALLBACK(folderview_tree_collapsed),
506 g_signal_connect(G_OBJECT(ctree), "resize_column",
507 G_CALLBACK(folderview_col_resized),
511 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
512 folderview_drag_types, 2,
513 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
514 g_signal_connect(G_OBJECT(ctree), "drag_motion",
515 G_CALLBACK(folderview_drag_motion_cb),
517 g_signal_connect(G_OBJECT(ctree), "drag_leave",
518 G_CALLBACK(folderview_drag_leave_cb),
520 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
521 G_CALLBACK(folderview_drag_received_cb),
523 g_signal_connect(G_OBJECT(ctree), "drag_end",
524 G_CALLBACK(folderview_drag_end_cb),
527 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
532 void folderview_set_column_order(FolderView *folderview)
534 GtkWidget *ctree = folderview->ctree;
535 FolderItem *item = folderview_get_selected_item(folderview);
536 FolderItem *sel_item = NULL, *op_item = NULL;
537 GtkWidget *scrolledwin = folderview->scrolledwin;
539 if (folderview->drag_timer_id != 0) {
540 g_source_remove(folderview->drag_timer_id);
541 folderview->drag_timer_id = 0;
543 if (folderview->deferred_refresh_id != 0) {
544 g_source_remove(folderview->deferred_refresh_id);
545 folderview->deferred_refresh_id = 0;
547 if (folderview->scroll_timeout_id != 0) {
548 g_source_remove(folderview->scroll_timeout_id);
549 folderview->scroll_timeout_id = 0;
551 if (folderview->postpone_select_id != 0) {
552 g_source_remove(folderview->postpone_select_id);
553 folderview->postpone_select_id = 0;
556 if (folderview->selected)
557 sel_item = folderview_get_selected_item(folderview);
558 if (folderview->opened)
559 op_item = folderview_get_opened_item(folderview);
561 debug_print("recreating tree...\n");
562 gtk_widget_destroy(folderview->ctree);
565 folderview->ctree = ctree = folderview_ctree_create(folderview);
566 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
567 GTK_CMCLIST(ctree)->hadjustment);
568 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
569 GTK_CMCLIST(ctree)->vadjustment);
570 gtk_widget_show(ctree);
573 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
575 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
577 folderview_set(folderview);
578 folderview_column_set_titles(folderview);
580 folderview_select(folderview,item);
583 FolderView *folderview_create(void)
585 FolderView *folderview;
586 GtkWidget *scrolledwin;
589 debug_print("Creating folder view...\n");
590 folderview = g_new0(FolderView, 1);
592 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
593 gtk_scrolled_window_set_policy
594 (GTK_SCROLLED_WINDOW(scrolledwin),
595 GTK_POLICY_AUTOMATIC,
596 prefs_common.folderview_vscrollbar_policy);
597 gtk_widget_set_size_request(scrolledwin,
598 prefs_common.folderview_width,
599 prefs_common.folderview_height);
601 folderview->scrolledwin = scrolledwin;
602 ctree = folderview_ctree_create(folderview);
604 /* create popup factories */
605 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
606 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
608 folderview->ctree = ctree;
610 folderview->folder_update_callback_id =
611 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
612 folderview->folder_item_update_callback_id =
613 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
615 gtk_widget_show_all(scrolledwin);
617 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
618 folderview_list = g_list_append(folderview_list, folderview);
620 folderview->drag_timer_id = 0;
621 folderview->deferred_refresh_id = 0;
622 folderview->scroll_timeout_id = 0;
623 folderview->postpone_select_id = 0;
628 void folderview_init(FolderView *folderview)
630 GtkWidget *ctree = folderview->ctree;
632 PangoFontDescription *normal_font;
634 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
635 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
636 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
637 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
638 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
639 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
640 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
641 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
642 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
643 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
644 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
645 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
646 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
647 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
648 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
649 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
650 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
651 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
652 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
653 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
654 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
655 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
656 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
657 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
658 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
660 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
661 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
662 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
663 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
664 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
665 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
666 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
667 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
668 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
675 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
676 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
677 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
678 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
679 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
680 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
681 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
682 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN_MARK, &m_foldersubsopenxpm);
683 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
685 normal_font = pango_font_description_from_string(NORMAL_FONT);
687 gtk_widget_modify_font(ctree, normal_font);
688 pango_font_description_free(normal_font);
690 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
693 PangoFontDescription *font_desc;
694 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
695 font_desc = pango_font_description_from_string(NORMAL_FONT);
697 if (normal_style->font_desc)
698 pango_font_description_free
699 (normal_style->font_desc);
700 normal_style->font_desc = font_desc;
702 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
703 normal_color_style = gtk_style_copy(normal_style);
704 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
708 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
709 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
710 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
711 PangoFontDescription *font_desc;
712 font_desc = pango_font_description_from_string(NORMAL_FONT);
714 pango_font_description_free(bold_style->font_desc);
715 bold_style->font_desc = font_desc;
717 pango_font_description_set_weight
718 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
720 PangoFontDescription *font_desc;
721 font_desc = pango_font_description_from_string(BOLD_FONT);
723 if (bold_style->font_desc)
724 pango_font_description_free
725 (bold_style->font_desc);
726 bold_style->font_desc = font_desc;
729 bold_color_style = gtk_style_copy(bold_style);
730 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
732 bold_tgtfold_style = gtk_style_copy(bold_style);
733 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
737 static gboolean folderview_defer_set(gpointer data)
739 FolderView *folderview = (FolderView *)data;
740 MainWindow *mainwin = folderview->mainwin;
744 if (mainwin->lock_count)
747 debug_print("doing deferred folderview_set now\n");
748 folderview_set(folderview);
750 folderview->deferred_refresh_id = 0;
754 void folderview_set(FolderView *folderview)
756 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
757 MainWindow *mainwin = folderview->mainwin;
758 FolderItem *sel_item = NULL, *op_item = NULL;
763 if (mainwin->lock_count) {
764 if (folderview->deferred_refresh_id == 0)
765 folderview->deferred_refresh_id =
766 g_timeout_add(500, folderview_defer_set, folderview);
767 debug_print("deferred folderview_set\n");
772 debug_print("Setting folder info...\n");
773 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
775 main_window_cursor_wait(mainwin);
777 if (folderview->selected)
778 sel_item = folderview_get_selected_item(folderview);
779 if (folderview->opened)
780 op_item = folderview_get_opened_item(folderview);
782 folderview->selected = NULL;
783 folderview->opened = NULL;
785 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
786 gtk_cmclist_clear(GTK_CMCLIST(ctree));
788 folderview_set_folders(folderview);
791 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
793 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
795 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
796 main_window_cursor_normal(mainwin);
797 STATUSBAR_POP(mainwin);
801 void folderview_set_all(void)
805 for (list = folderview_list; list != NULL; list = list->next)
806 folderview_set((FolderView *)list->data);
809 void folderview_select(FolderView *folderview, FolderItem *item)
811 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
812 GtkCMCTreeNode *node;
813 GtkCMCTreeNode *old_selected = folderview->selected;
817 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
818 if (node) folderview_select_node(folderview, node);
820 if (old_selected != node)
821 folder_update_op_count();
824 static void mark_all_read_cb(GtkAction *action, gpointer data)
826 mark_all_read_handler(action, data, FALSE);
829 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
831 mark_all_read_handler(action, data, TRUE);
834 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
836 FolderView *folderview = (FolderView *)data;
841 item = folderview_get_selected_item(folderview);
845 message = recursive? _("Do you really want to mark all mails in this "
846 "folder and its sub-folders as read?") :
847 _("Do you really want to mark all mails in this "
849 if (folderview->summaryview->folder_item != item &&
850 prefs_common.ask_mark_all_read) {
851 val = alertpanel_full(_("Mark all as read"),
852 message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
853 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
855 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
857 else if (val & G_ALERTDISABLE)
858 prefs_common.ask_mark_all_read = FALSE;
861 folder_item_update_freeze();
862 if (folderview->summaryview->folder_item != item && !recursive)
863 summary_lock(folderview->summaryview);
865 summary_freeze(folderview->summaryview);
868 folderutils_mark_all_read_recursive(item);
870 folderutils_mark_all_read(item);
872 if (folderview->summaryview->folder_item != item && !recursive)
873 summary_unlock(folderview->summaryview);
875 summary_thaw(folderview->summaryview);
876 folder_item_update_thaw();
879 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
881 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
883 cm_return_if_fail(node != NULL);
885 if (folderview->open_folder) {
889 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
890 gtkut_ctree_expand_parent_all(ctree, node);
892 folderview->open_folder = TRUE;
893 gtkut_ctree_set_focus_row(ctree, node);
894 gtk_cmctree_select(ctree, node);
895 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
896 if ((folderview->summaryview->folder_item &&
897 folderview->summaryview->folder_item->total_msgs > 0) ||
898 prefs_common.layout_mode == SMALL_LAYOUT)
899 summary_grab_focus(folderview->summaryview);
901 gtk_widget_grab_focus(folderview->ctree);
904 void folderview_unselect(FolderView *folderview)
906 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
908 (GTK_CMCTREE(folderview->ctree), folderview->opened);
910 folderview->selected = folderview->opened = NULL;
913 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
914 GtkCMCTreeNode *node,
920 node = gtkut_ctree_node_next(ctree, node);
922 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
924 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
925 item = gtk_cmctree_node_get_row_data(ctree, node);
928 if (item->stype == F_TRASH || item->stype == F_DRAFT)
932 if(item->unread_msgs > 0)
936 if(item->new_msgs > 0)
940 if(item->marked_msgs > 0)
949 void folderview_select_next_with_flag(FolderView *folderview,
953 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
954 GtkCMCTreeNode *node = NULL;
955 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
956 gboolean last_open = prefs_common.always_show_msg;
960 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
963 prefs_common.summary_select_prio[0] = ACTION_NEW;
966 prefs_common.summary_select_prio[0] = ACTION_MARKED;
969 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
971 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
973 folderview_select_node(folderview, node);
977 if (!folderview->opened ||
978 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
982 /* search again from the first node */
983 node = folderview_find_next_with_flag(ctree, NULL, flag);
985 folderview_select_node(folderview, node);
988 prefs_common.summary_select_prio[0] = last_summary_select_prio;
989 prefs_common.always_show_msg = last_open;
992 FolderItem *folderview_get_selected_item(FolderView *folderview)
994 g_return_val_if_fail(folderview != NULL, NULL);
995 g_return_val_if_fail(folderview->ctree != NULL, NULL);
997 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
999 if (!folderview->selected) return NULL;
1000 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1003 FolderItem *folderview_get_opened_item(FolderView *folderview)
1005 g_return_val_if_fail(folderview != NULL, NULL);
1006 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1008 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1010 if (!folderview->opened) return NULL;
1011 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1014 static void folderview_set_folders(FolderView *folderview)
1017 list = folder_get_list();
1019 for (; list != NULL; list = list->next) {
1020 folderview_append_folder(folderview, FOLDER(list->data));
1024 static gchar *get_scan_str(FolderItem *item)
1027 return g_strdup_printf(_("Scanning folder %s/%s..."),
1028 item->folder->name, item->path);
1030 return g_strdup_printf(_("Scanning folder %s..."),
1031 item->folder->name);
1033 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1037 for (list = folderview_list; list != NULL; list = list->next) {
1038 FolderView *folderview = (FolderView *)list->data;
1039 MainWindow *mainwin = folderview->mainwin;
1040 gchar *str = get_scan_str(item);
1042 STATUSBAR_PUSH(mainwin, str);
1043 STATUSBAR_POP(mainwin);
1048 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1051 MainWindow *mainwin = mainwindow_get_mainwindow();
1052 FolderView *folderview = NULL;
1053 GtkAdjustment *pos = NULL;
1056 cm_return_if_fail(folder != NULL);
1058 if (!folder->klass->scan_tree) return;
1061 alertpanel_full(_("Rebuild folder tree"),
1062 _("Rebuilding the folder tree will remove "
1063 "local caches. Do you want to continue?"),
1064 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1065 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1066 != G_ALERTALTERNATE) {
1072 window = label_window_create(_("Rebuilding folder tree..."));
1074 window = label_window_create(_("Scanning folder tree..."));
1077 folderview = mainwin->folderview;
1080 pos = gtk_scrolled_window_get_vadjustment(
1081 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1082 height = gtk_adjustment_get_value(pos);
1085 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1086 folder_scan_tree(folder, rebuild);
1087 folder_set_ui_func(folder, NULL, NULL);
1089 folderview_set_all();
1092 pos = gtk_scrolled_window_get_vadjustment(
1093 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1094 gtk_adjustment_set_value(pos, height);
1095 gtk_adjustment_changed(pos);
1097 label_window_destroy(window);
1101 /** folderview_check_new()
1102 * Scan and update the folder and return the
1103 * count the number of new messages since last check.
1104 * \param folder the folder to check for new messages
1105 * \return the number of new messages since last check
1107 gint folderview_check_new(Folder *folder)
1111 FolderView *folderview;
1113 GtkCMCTreeNode *node;
1115 gint former_new_msgs = 0;
1116 gint former_new = 0, former_unread = 0, former_total;
1118 for (list = folderview_list; list != NULL; list = list->next) {
1119 folderview = (FolderView *)list->data;
1120 ctree = GTK_CMCTREE(folderview->ctree);
1121 folderview->scanning_folder = folder;
1123 main_window_lock(folderview->mainwin);
1125 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1126 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1128 item = gtk_cmctree_node_get_row_data(ctree, node);
1129 if (!item || !item->path || !item->folder) continue;
1130 if (item->no_select) continue;
1131 if (folder && folder != item->folder) continue;
1132 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1133 if (!item->prefs->newmailcheck) continue;
1134 if (item->processing_pending == TRUE) {
1135 debug_print("skipping %s, processing pending\n",
1136 item->path ? item->path : item->name);
1139 if (item->scanning != ITEM_NOT_SCANNING) {
1140 debug_print("skipping %s, scanning\n",
1141 item->path ? item->path : item->name);
1145 str = get_scan_str(item);
1147 STATUSBAR_PUSH(folderview->mainwin, str);
1151 folderview_scan_tree_func(item->folder, item, NULL);
1152 former_new = item->new_msgs;
1153 former_unread = item->unread_msgs;
1154 former_total = item->total_msgs;
1156 if (item->folder->klass->scan_required &&
1157 (item->folder->klass->scan_required(item->folder, item) ||
1158 item->folder->inbox == item ||
1159 item->opened == TRUE ||
1160 item->processing_pending == TRUE)) {
1161 if (folder_item_scan(item) < 0) {
1163 summaryview_unlock(folderview->summaryview, item);
1164 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1165 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1166 item->path ? item->path:item->name);
1167 STATUSBAR_POP(folderview->mainwin);
1169 } else if (!FOLDER_IS_LOCAL(folder)) {
1170 STATUSBAR_POP(folderview->mainwin);
1175 } else if (!item->folder->klass->scan_required) {
1176 if (folder_item_scan(item) < 0) {
1177 summaryview_unlock(folderview->summaryview, item);
1178 if (folder && !FOLDER_IS_LOCAL(folder)) {
1179 STATUSBAR_POP(folderview->mainwin);
1184 if (former_new != item->new_msgs ||
1185 former_unread != item->unread_msgs ||
1186 former_total != item->total_msgs)
1187 folderview_update_node(folderview, node);
1189 new_msgs += item->new_msgs;
1190 former_new_msgs += former_new;
1191 STATUSBAR_POP(folderview->mainwin);
1193 folderview->scanning_folder = NULL;
1194 main_window_unlock(folderview->mainwin);
1198 folder_write_list();
1199 /* Number of new messages since last check is the just the difference
1200 * between former_new_msgs and new_msgs. If new_msgs is less than
1201 * former_new_msgs, that would mean another session accessed the folder
1202 * and the result is not well defined.
1204 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1208 void folderview_check_new_all(void)
1212 FolderView *folderview;
1214 folderview = (FolderView *)folderview_list->data;
1217 main_window_lock(folderview->mainwin);
1218 window = label_window_create
1219 (_("Checking for new messages in all folders..."));
1221 list = folder_get_list();
1222 for (; list != NULL; list = list->next) {
1223 Folder *folder = list->data;
1225 folderview_check_new(folder);
1228 folder_write_list();
1229 folderview_set_all();
1231 label_window_destroy(window);
1232 main_window_unlock(folderview->mainwin);
1236 static gboolean folderview_have_children_sub(FolderView *folderview,
1242 if (!item || !item->folder || !item->folder->node)
1245 node = item->folder->node;
1247 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1248 node = node->children;
1250 if (in_sub && item->total_msgs > 0) {
1254 while (node != NULL) {
1255 if (node && node->data) {
1256 FolderItem *next_item = (FolderItem*) node->data;
1258 if (folderview_have_children_sub(folderview,
1267 static gboolean folderview_have_children(FolderView *folderview,
1270 return folderview_have_children_sub(folderview, item, FALSE);
1273 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1279 if (!item || !item->folder || !item->folder->node)
1282 node = item->folder->node;
1284 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1285 node = node->children;
1288 (item->new_msgs > 0 ||
1289 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1293 while (node != NULL) {
1294 if (node && node->data) {
1295 FolderItem *next_item = (FolderItem*) node->data;
1297 if (folderview_have_new_children_sub(folderview,
1306 static gboolean folderview_have_new_children(FolderView *folderview,
1309 return folderview_have_new_children_sub(folderview, item, FALSE);
1312 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1318 if (!item || !item->folder || !item->folder->node)
1321 node = item->folder->node;
1323 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1324 node = node->children;
1327 (item->unread_msgs > 0 ||
1328 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1332 while (node != NULL) {
1333 if (node && node->data) {
1334 FolderItem *next_item = (FolderItem*) node->data;
1336 if (folderview_have_unread_children_sub(folderview,
1346 static gboolean folderview_have_unread_children(FolderView *folderview,
1349 return folderview_have_unread_children_sub(folderview, item, FALSE);
1352 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1358 if (!item || !item->folder || !item->folder->node)
1361 node = item->folder->node;
1363 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1364 node = node->children;
1366 if (in_sub && item->search_match){
1370 while (node != NULL) {
1371 if (node && node->data) {
1372 FolderItem *next_item = (FolderItem*) node->data;
1374 if (folderview_have_matching_children_sub(folderview,
1384 static gboolean folderview_have_matching_children(FolderView *folderview,
1387 return folderview_have_matching_children_sub(folderview, item, FALSE);
1390 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1396 if (!item || !item->folder || !item->folder->node)
1399 node = item->folder->node;
1401 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1402 node = node->children;
1404 if (item->marked_msgs != 0) {
1408 while (node != NULL) {
1409 if (node && node->data) {
1410 FolderItem *next_item = (FolderItem*) node->data;
1412 if (folderview_have_marked_children_sub(folderview,
1421 static gboolean folderview_have_marked_children(FolderView *folderview,
1424 return folderview_have_marked_children_sub(folderview, item, FALSE);
1427 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1429 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1430 GtkStyle *style = NULL;
1431 GtkStyle *color_style = NULL;
1433 GdkPixbuf *xpm, *openxpm;
1434 static GdkPixbuf *searchicon;
1435 gboolean mark = FALSE;
1438 gboolean add_unread_mark;
1439 gboolean add_sub_match_mark;
1440 gboolean use_bold, use_color;
1441 gint *col_pos = folderview->col_pos;
1442 SpecialFolderItemType stype;
1444 item = gtk_cmctree_node_get_row_data(ctree, node);
1445 cm_return_if_fail(item != NULL);
1447 if (!GTK_CMCTREE_ROW(node)->expanded)
1448 mark = folderview_have_marked_children(folderview, item);
1450 mark = (item->marked_msgs != 0);
1452 stype = item->stype;
1453 if (stype == F_NORMAL) {
1454 if (folder_has_parent_of_type(item, F_TRASH))
1456 else if (folder_has_parent_of_type(item, F_DRAFT))
1458 else if (folder_has_parent_of_type(item, F_OUTBOX))
1460 else if (folder_has_parent_of_type(item, F_QUEUE))
1465 if (item->hide_read_msgs || item->hide_read_threads) {
1466 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1467 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1469 xpm = mark?m_inboxxpm:inboxxpm;
1470 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1474 if (item->hide_read_msgs || item->hide_read_threads) {
1475 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1476 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1478 xpm = mark?m_outboxxpm:outboxxpm;
1479 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1483 if (item->hide_read_msgs || item->hide_read_threads) {
1484 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1485 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1487 xpm = mark?m_queuexpm:queuexpm;
1488 openxpm = mark?m_queueopenxpm:queueopenxpm;
1492 if (item->hide_read_msgs || item->hide_read_threads) {
1493 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1494 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1496 xpm = mark?m_trashxpm:trashxpm;
1497 openxpm = mark?m_trashopenxpm:trashopenxpm;
1501 xpm = mark?m_draftsxpm:draftsxpm;
1502 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1506 FOLDER_TYPE(item->folder) == F_IMAP &&
1507 item->folder->account->imap_subsonly) {
1508 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1509 openxpm = mark?m_foldersubsopenxpm:foldersubsopenxpm;
1510 } else if (item->hide_read_msgs || item->hide_read_threads) {
1511 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1512 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1514 xpm = mark?m_folderxpm:folderxpm;
1515 openxpm = mark?m_folderopenxpm:folderopenxpm;
1519 if (item->no_select) {
1520 xpm = openxpm = noselectxpm;
1523 name = folder_item_get_name(item);
1525 if (!GTK_CMCTREE_ROW(node)->expanded) {
1526 add_unread_mark = folderview_have_unread_children(
1528 add_sub_match_mark = folderview_have_matching_children(
1531 add_unread_mark = FALSE;
1532 add_sub_match_mark = FALSE;
1535 if (item->search_match) {
1537 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1540 xpm = openxpm = searchicon;
1544 if (prefs_common.display_folder_unread) {
1545 if (folder_has_parent_of_type(item, F_QUEUE)) {
1546 /* only total_msgs matters here */
1547 if (item->total_msgs > 0) {
1548 /* show total number (should be equal to the unread number)
1550 str = g_strdup_printf("%s (%d%s%s)",
1551 name, item->total_msgs,
1552 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1553 (item->unreadmarked_msgs > 0) ? "!" : "");
1556 if (prefs_common.display_folder_unread == 1) {
1557 if (item->unread_msgs > 0) {
1558 /* show unread number and signs */
1559 str = g_strdup_printf("%s (%d%s%s)",
1560 name, item->unread_msgs,
1561 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1562 (item->unreadmarked_msgs > 0) ? "!" : "");
1565 if (item->total_msgs > 0) {
1566 /* show unread number, total number and signs if any */
1567 str = g_strdup_printf("%s (%d/%d%s%s)",
1568 name, item->unread_msgs, item->total_msgs,
1569 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1570 (item->unreadmarked_msgs > 0) ? "!" : "");
1574 if ((str == NULL) &&
1575 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1576 /* no unread/total numbers, but at least one sign */
1577 str = g_strdup_printf("%s (%s%s)",
1579 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1580 (item->unreadmarked_msgs > 0) ? "!" : "");
1584 /* last fallback, folder name only or with +! sign */
1585 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1586 str = g_strdup_printf("%s%s",
1588 } else if (item->unreadmarked_msgs > 0) {
1589 str = g_strdup_printf("%s%s",
1591 } else if (add_sub_match_mark) {
1592 str = g_strdup_printf("%s%s",
1595 str = g_strdup_printf("%s", name);
1598 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1600 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1604 if (!folder_item_parent(item)) {
1605 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1606 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1607 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1609 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1610 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1611 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1614 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1615 folder_has_parent_of_type(item, F_TRASH)) {
1616 use_bold = use_color = FALSE;
1617 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1618 GSList *list = folder_item_get_msg_list(item);
1620 use_bold = use_color = FALSE;
1621 for (cur = list; cur; cur = cur->next) {
1622 MsgInfo *msginfo = (MsgInfo *)cur->data;
1623 if (!MSG_IS_DELETED(msginfo->flags)) {
1624 /* highlight queue folder if there are any messages */
1625 use_bold = use_color = TRUE;
1629 if (!GTK_CMCTREE_ROW(node)->expanded &&
1630 use_bold == FALSE &&
1631 folderview_have_children(folderview, item))
1632 use_bold = use_color = TRUE;
1633 procmsg_msg_list_free(list);
1635 /* if unread messages exist, print with bold font */
1636 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1638 /* if new messages exist, print with colored letter */
1640 (item->new_msgs > 0) ||
1642 folderview_have_new_children(folderview, item));
1645 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1650 if (item->prefs->color > 0 && !use_color) {
1651 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1652 color_style = gtk_style_copy(bold_style);
1653 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1654 style = color_style;
1655 } else if (use_color) {
1656 style = bold_color_style;
1659 if (item->op_count > 0) {
1660 style = bold_tgtfold_style;
1662 } else if (use_color) {
1663 style = normal_color_style;
1664 gtk_cmctree_node_set_foreground(ctree, node,
1665 &folderview->color_new);
1666 } else if (item->op_count > 0) {
1667 style = bold_tgtfold_style;
1668 } else if (item->prefs->color > 0) {
1670 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1671 color_style = gtk_style_copy(normal_style);
1672 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1673 style = color_style;
1675 style = normal_style;
1678 gtk_cmctree_node_set_row_style(ctree, node, style);
1680 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1681 folderview_update_node(folderview, node);
1684 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1687 FolderView *folderview;
1689 GtkCMCTreeNode *node;
1691 cm_return_if_fail(item != NULL);
1693 for (list = folderview_list; list != NULL; list = list->next) {
1694 folderview = (FolderView *)list->data;
1695 ctree = GTK_CMCTREE(folderview->ctree);
1697 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1698 if (node && item->search_match != matches) {
1699 item->search_match = matches;
1700 folderview_update_node(folderview, node);
1705 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1707 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1708 FolderView *folderview = (FolderView *)data;
1710 GtkCMCTreeNode *node;
1711 cm_return_val_if_fail(update_info != NULL, TRUE);
1712 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1713 cm_return_val_if_fail(folderview != NULL, FALSE);
1715 ctree = GTK_CMCTREE(folderview->ctree);
1717 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1720 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1721 folderview_update_node(folderview, node);
1723 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1724 update_info->item == folderview->summaryview->folder_item &&
1725 update_info->item != NULL)
1726 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1727 summary_show(folderview->summaryview, update_info->item);
1733 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1734 GNode *gnode, GtkCMCTreeNode *cnode,
1737 FolderView *folderview = (FolderView *)data;
1738 FolderItem *item = FOLDER_ITEM(gnode->data);
1740 cm_return_val_if_fail(item != NULL, FALSE);
1742 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1743 folderview_update_node(folderview, cnode);
1748 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1751 FolderView *folderview = (FolderView *)data;
1754 if (GTK_CMCTREE_ROW(node)->children) {
1755 item = gtk_cmctree_node_get_row_data(ctree, node);
1756 cm_return_if_fail(item != NULL);
1758 if (!item->collapsed)
1759 gtk_cmctree_expand(ctree, node);
1761 folderview_update_node(folderview, node);
1765 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1766 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1769 GtkCMCTreeNode *node, *parent, *sibling;
1771 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1773 g_warning("%s not found.", item->path);
1775 parent = GTK_CMCTREE_ROW(node)->parent;
1776 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1777 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1779 sibling = GTK_CMCTREE_ROW(parent)->children;
1783 tmp = gtk_cmctree_node_get_row_data
1785 if (tmp && tmp->stype != F_NORMAL)
1786 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1790 if (node != sibling)
1791 gtk_cmctree_move(ctree, node, parent, sibling);
1798 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1801 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1802 GtkCMCTreeNode *prev = NULL;
1804 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1805 gtk_sctree_sort_recursive(ctree, root);
1806 if (root && GTK_CMCTREE_ROW(root)->parent) {
1807 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1810 set_special_folder(ctree, folder->inbox, root, &prev);
1811 set_special_folder(ctree, folder->outbox, root, &prev);
1812 set_special_folder(ctree, folder->draft, root, &prev);
1813 set_special_folder(ctree, folder->queue, root, &prev);
1814 set_special_folder(ctree, folder->trash, root, &prev);
1815 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1818 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1820 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1821 GtkCMCTreeNode *root;
1823 cm_return_if_fail(folder != NULL);
1825 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1826 folderview_gnode_func, folderview);
1827 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1829 folderview_sort_folders(folderview, root, folder);
1832 /* callback functions */
1833 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1834 GdkEventButton *event)
1838 FolderViewPopup *fpopup;
1839 GtkActionGroup *action_group;
1841 FolderItem *special_trash = NULL, *special_queue = NULL;
1843 GtkUIManager *ui_manager = gtk_ui_manager_new();
1845 if (folderview->ui_manager)
1846 g_object_unref(folderview->ui_manager);
1848 folderview->ui_manager = ui_manager;
1849 item = folderview_get_selected_item(folderview);
1851 cm_return_if_fail(item != NULL);
1852 cm_return_if_fail(item->folder != NULL);
1853 folder = item->folder;
1855 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1858 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1860 fpopup = g_hash_table_lookup(folderview_popups, "common");
1861 action_group = g_hash_table_lookup(folderview->popups, "common");
1864 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1865 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1866 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1868 if (fpopup->add_menuitems)
1869 fpopup->add_menuitems(ui_manager, item);
1871 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1873 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1874 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1875 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1876 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1877 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1879 if (fpopup->set_sensitivity != NULL)
1880 fpopup->set_sensitivity(ui_manager, item);
1882 if (NULL != (ac = account_find_from_item(item))) {
1883 special_trash = account_get_special_folder(ac, F_TRASH);
1884 special_queue = account_get_special_folder(ac, F_QUEUE);
1887 if ((item == folder->trash || item == special_trash
1888 || folder_has_parent_of_type(item, F_TRASH))) {
1889 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1890 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1893 if ((item == folder->queue || item == special_queue
1894 || folder_has_parent_of_type(item, F_QUEUE))) {
1895 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1896 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1899 #define SET_SENS(name, sens) \
1900 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1902 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1903 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1904 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1905 folderview->selected == folderview->opened);
1906 SET_SENS("FolderViewPopup/Properties", TRUE);
1908 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1909 item->total_msgs >= 1 && !item->processing_pending);
1910 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1911 !item->no_select && !item->processing_pending);
1913 if (item == folder->trash || item == special_trash
1914 || folder_has_parent_of_type(item, F_TRASH)) {
1915 GSList *msglist = folder_item_get_msg_list(item);
1916 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1917 procmsg_msg_list_free(msglist);
1919 if (item == folder->queue || item == special_queue
1920 || folder_has_parent_of_type(item, F_QUEUE)) {
1921 GSList *msglist = folder_item_get_msg_list(item);
1922 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1923 procmsg_msg_list_free(msglist);
1927 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1928 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1929 g_signal_connect(G_OBJECT(popup), "selection_done",
1930 G_CALLBACK(folderview_popup_close),
1932 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1933 event->button, event->time);
1936 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1937 FolderView *folderview)
1939 GtkCMCList *clist = GTK_CMCLIST(ctree);
1940 gint prev_row = -1, row = -1, column = -1;
1942 if (!event) return FALSE;
1944 if (event->button == 1 || event->button == 2) {
1945 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1946 folderview->open_folder = TRUE;
1948 if (event->type == GDK_2BUTTON_PRESS) {
1949 if (clist->selection) {
1950 GtkCMCTreeNode *node;
1952 node = GTK_CMCTREE_NODE(clist->selection->data);
1954 gtk_cmctree_toggle_expansion(
1957 folderview->open_folder = FALSE;
1964 if (event->button == 2 || event->button == 3) {
1966 if (clist->selection) {
1967 GtkCMCTreeNode *node;
1969 node = GTK_CMCTREE_NODE(clist->selection->data);
1971 prev_row = gtkut_ctree_get_nth_from_node
1972 (GTK_CMCTREE(ctree), node);
1975 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1978 if (prev_row != row) {
1979 gtk_cmclist_unselect_all(clist);
1980 if (event->button == 2)
1981 folderview_select_node
1983 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1986 gtk_cmclist_select_row(clist, row, column);
1990 if (event->button != 3) return FALSE;
1992 folderview_set_sens_and_popup_menu(folderview, row, event);
1996 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1997 FolderView *folderview)
1999 int row = -1, column = -1;
2001 if (!event) return FALSE;
2003 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2006 if (event->button == 1 && folderview->open_folder == FALSE &&
2007 folderview->opened != NULL) {
2008 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2009 folderview->opened);
2010 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2016 #define BREAK_ON_MODIFIER_KEY() \
2017 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2019 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2020 FolderView *folderview)
2022 GtkCMCTreeNode *node;
2025 if (!event) return FALSE;
2027 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2030 switch (event->keyval) {
2032 if (folderview->selected) {
2033 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2034 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2035 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2036 folderview->selected);
2038 folderview_select_node(folderview,
2039 folderview->selected);
2043 case GDK_KEY_Return:
2044 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2045 gtk_cmctree_toggle_expansion(
2046 GTK_CMCTREE(folderview->ctree),
2047 folderview->selected);
2051 case GDK_KEY_Return:
2052 case GDK_KEY_KP_Enter:
2053 if (folderview->selected)
2054 folderview_select_node(folderview, folderview->selected);
2058 BREAK_ON_MODIFIER_KEY();
2059 if (folderview->selected) {
2060 if (folderview->opened == folderview->selected &&
2061 (!folderview->summaryview->folder_item ||
2062 folderview->summaryview->folder_item->total_msgs == 0))
2063 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2065 folderview_select_node(folderview,
2066 folderview->selected);
2070 if (folderview->selected) {
2071 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2072 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2073 folderview->selected);
2075 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2076 folderview->selected))) {
2077 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2078 NULL, folder_item_parent(item)))) {
2079 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2080 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2081 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2090 if (event->keyval == GDK_KEY_Home)
2091 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2093 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2094 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2096 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2098 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2099 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2109 typedef struct _PostponedSelectData
2112 GtkCMCTreeNode *row;
2114 FolderView *folderview;
2115 } PostponedSelectData;
2117 static gboolean postpone_select(void *data)
2119 PostponedSelectData *psdata = (PostponedSelectData *)data;
2120 debug_print("trying again\n");
2122 psdata->folderview->postpone_select_id = 0;
2123 psdata->folderview->open_folder = TRUE;
2124 main_window_cursor_normal(psdata->folderview->mainwin);
2125 STATUSBAR_POP(psdata->folderview->mainwin);
2126 folderview_selected(psdata->ctree, psdata->row,
2127 psdata->column, psdata->folderview);
2132 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2134 if (folderview->opened) {
2136 folderview->opened = NULL;
2140 FolderItem *olditem =
2141 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2142 folderview->opened);
2144 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2145 olditem->path ? olditem->path:olditem->name);
2146 /* will be null if we just moved the previously opened folder */
2147 STATUSBAR_PUSH(folderview->mainwin, buf);
2148 main_window_cursor_wait(folderview->mainwin);
2150 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2151 summary_show(folderview->summaryview, NULL);
2152 folder_item_close(olditem);
2153 main_window_cursor_normal(folderview->mainwin);
2154 STATUSBAR_POP(folderview->mainwin);
2155 if (olditem->folder->klass->item_closed)
2156 olditem->folder->klass->item_closed(olditem);
2161 if (folderview->opened &&
2162 !GTK_CMCTREE_ROW(folderview->opened)->children)
2163 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2165 folderview->opened = NULL;
2167 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2168 gint column, FolderView *folderview)
2170 static gboolean can_select = TRUE; /* exclusive lock */
2175 GtkCMCTreeNode *old_opened = folderview->opened;
2177 folderview->selected = row;
2179 debug_print("newly selected %p, opened %p\n", folderview->selected,
2180 folderview->opened);
2181 if (folderview->opened == row) {
2182 folderview->open_folder = FALSE;
2187 item = gtk_cmctree_node_get_row_data(ctree, row);
2190 folderview->open_folder = FALSE;
2194 if (!can_select || summary_is_locked(folderview->summaryview)) {
2195 if (folderview->opened) {
2196 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2197 gtk_cmctree_select(ctree, folderview->opened);
2199 folderview->open_folder = FALSE;
2204 if (!folderview->open_folder) {
2211 /* Save cache for old folder */
2212 /* We don't want to lose all caches if sylpheed crashed */
2213 /* resets folderview->opened to NULL */
2214 folderview_close_opened(folderview, FALSE);
2216 /* CLAWS: set compose button type: news folder items
2217 * always have a news folder as parent */
2219 toolbar_set_compose_button
2220 (folderview->mainwin->toolbar,
2221 FOLDER_TYPE(item->folder) == F_NEWS ?
2222 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2225 debug_print("Folder %s is selected\n", item->path);
2227 if (!GTK_CMCTREE_ROW(row)->children)
2228 gtk_cmctree_expand(ctree, row);
2230 /* ungrab the mouse event */
2231 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2232 gtk_grab_remove(GTK_WIDGET(ctree));
2233 if (gdk_pointer_is_grabbed())
2234 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2238 /* TODO: wwp: avoid displaying (null) in the status bar */
2239 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2240 item->path : "(null)");
2241 debug_print("%s\n", buf);
2242 STATUSBAR_PUSH(folderview->mainwin, buf);
2245 main_window_cursor_wait(folderview->mainwin);
2247 if (folderview->scanning_folder == item->folder) {
2250 res = folder_item_open(item);
2253 if (res == -1 && item->no_select == FALSE) {
2254 main_window_cursor_normal(folderview->mainwin);
2255 STATUSBAR_POP(folderview->mainwin);
2257 alertpanel_error(_("Folder could not be opened."));
2259 folderview->open_folder = FALSE;
2263 } else if (res == -2 && item->no_select == FALSE) {
2264 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2265 data->ctree = ctree;
2267 data->column = column;
2268 data->folderview = folderview;
2269 debug_print("postponing open of %s till end of scan\n",
2270 item->path ? item->path:item->name);
2271 folderview->open_folder = FALSE;
2273 if (folderview->postpone_select_id != 0)
2274 g_source_remove(folderview->postpone_select_id);
2275 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2280 main_window_cursor_normal(folderview->mainwin);
2283 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2284 opened = summary_show(folderview->summaryview, item);
2286 folder_clean_cache_memory(item);
2289 gtkut_ctree_set_focus_row(ctree, old_opened);
2290 gtk_cmctree_select(ctree, old_opened);
2291 folderview->opened = old_opened;
2293 folderview->opened = row;
2294 if (gtk_cmctree_node_is_visible(ctree, row)
2295 != GTK_VISIBILITY_FULL)
2296 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2299 STATUSBAR_POP(folderview->mainwin);
2301 folderview->open_folder = FALSE;
2306 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2307 FolderView *folderview)
2311 item = gtk_cmctree_node_get_row_data(ctree, node);
2312 cm_return_if_fail(item != NULL);
2313 item->collapsed = FALSE;
2314 folderview_update_node(folderview, node);
2317 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2318 FolderView *folderview)
2322 item = gtk_cmctree_node_get_row_data(ctree, node);
2323 cm_return_if_fail(item != NULL);
2324 item->collapsed = TRUE;
2325 folderview_update_node(folderview, node);
2328 static void folderview_popup_close(GtkMenuShell *menu_shell,
2329 FolderView *folderview)
2331 if (!folderview->opened) return;
2333 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2336 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2337 FolderView *folderview)
2339 FolderColumnType type = folderview->col_state[column].type;
2341 prefs_common.folder_col_size[type] = width;
2344 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2346 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2347 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2348 GtkCMCTreeNode *node, *parent_node;
2349 gint *col_pos = folderview->col_pos;
2350 FolderItemUpdateData hookdata;
2352 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2353 if (parent_node == NULL)
2356 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2358 text[col_pos[F_COL_FOLDER]] = item->name;
2359 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2364 gtk_cmctree_expand(ctree, parent_node);
2365 gtk_cmctree_node_set_row_data(ctree, node, item);
2367 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2368 folderview_sort_folders(folderview, parent_node, item->folder);
2370 hookdata.item = item;
2371 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2372 hookdata.msg = NULL;
2373 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2375 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2378 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2380 FolderView *folderview = (FolderView *)data;
2382 GSList *mlist = NULL;
2384 FolderItem *special_trash = NULL;
2387 if (!folderview->selected) return;
2388 item = folderview_get_selected_item(folderview);
2389 cm_return_if_fail(item != NULL);
2390 cm_return_if_fail(item->folder != NULL);
2392 if (NULL != (ac = account_find_from_item(item)))
2393 special_trash = account_get_special_folder(ac, F_TRASH);
2395 if (item != item->folder->trash && item != special_trash
2396 && !folder_has_parent_of_type(item, F_TRASH)) return;
2398 if (prefs_common.ask_on_clean) {
2399 if (alertpanel(_("Empty trash"),
2400 _("Delete all messages in trash?"),
2401 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2405 mlist = folder_item_get_msg_list(item);
2407 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2408 MsgInfo * msginfo = (MsgInfo *) cur->data;
2409 if (MSG_IS_LOCKED(msginfo->flags))
2411 /* is it partially received? (partial_recv isn't cached) */
2412 if (msginfo->total_size != 0 &&
2413 msginfo->size != (off_t)msginfo->total_size)
2414 partial_mark_for_delete(msginfo);
2416 procmsg_msg_list_free(mlist);
2418 folder_item_remove_all_msg(item);
2421 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2423 FolderView *folderview = (FolderView *)data;
2425 FolderItem *special_queue = NULL;
2427 gchar *errstr = NULL;
2429 if (!folderview->selected) return;
2430 item = folderview_get_selected_item(folderview);
2431 cm_return_if_fail(item != NULL);
2432 cm_return_if_fail(item->folder != NULL);
2434 if (NULL != (ac = account_find_from_item(item)))
2435 special_queue = account_get_special_folder(ac, F_QUEUE);
2437 if (item != item->folder->queue && item != special_queue
2438 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2440 if (procmsg_queue_is_empty(item))
2443 if (prefs_common.work_offline)
2444 if (alertpanel(_("Offline warning"),
2445 _("You're working offline. Override?"),
2446 GTK_STOCK_NO, GTK_STOCK_YES,
2447 NULL) != G_ALERTALTERNATE)
2450 /* ask for confirmation before sending queued messages only
2451 in online mode and if there is at least one message queued
2452 in any of the folder queue
2454 if (prefs_common.confirm_send_queued_messages) {
2455 if (!prefs_common.work_offline) {
2456 if (alertpanel(_("Send queued messages"),
2457 _("Send all queued messages?"),
2458 GTK_STOCK_CANCEL, _("_Send"),
2459 NULL) != G_ALERTALTERNATE)
2464 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2466 alertpanel_error_log(_("Some errors occurred while "
2467 "sending queued messages."));
2469 alertpanel_error_log(_("Some errors occurred "
2470 "while sending queued messages:\n%s"), errstr);
2476 static void folderview_search_cb(GtkAction *action, gpointer data)
2478 FolderView *folderview = (FolderView *)data;
2479 summary_search(folderview->summaryview);
2482 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2484 FolderView *folderview = (FolderView *)data;
2487 if (!folderview->selected) return;
2489 item = folderview_get_selected_item(folderview);
2490 cm_return_if_fail(item != NULL);
2491 cm_return_if_fail(item->folder != NULL);
2493 item->processing_pending = TRUE;
2494 folder_item_apply_processing(item);
2495 item->processing_pending = FALSE;
2498 static void folderview_property_cb(GtkAction *action, gpointer data)
2500 FolderView *folderview = (FolderView *)data;
2503 if (!folderview->selected) return;
2505 item = folderview_get_selected_item(folderview);
2506 cm_return_if_fail(item != NULL);
2507 cm_return_if_fail(item->folder != NULL);
2509 prefs_folder_item_open(item);
2512 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2514 GSList *list = NULL;
2515 GSList *done = NULL;
2516 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2518 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2519 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2520 && list->data != node) {
2521 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2522 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2525 for (list = done; list != NULL; list = g_slist_next(list)) {
2526 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2532 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2533 FolderItem *to_folder, gboolean copy)
2535 FolderItem *new_folder = NULL;
2539 cm_return_if_fail(folderview != NULL);
2540 cm_return_if_fail(from_folder != NULL);
2541 cm_return_if_fail(to_folder != NULL);
2543 if (prefs_common.warn_dnd) {
2544 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2545 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2546 from_folder->name, to_folder->name);
2547 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2548 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2549 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2552 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2554 else if (status & G_ALERTDISABLE)
2555 prefs_common.warn_dnd = FALSE;
2558 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2559 from_folder->name, to_folder->name);
2560 STATUSBAR_PUSH(folderview->mainwin, buf);
2562 summary_clear_all(folderview->summaryview);
2563 folderview->opened = NULL;
2564 folderview->selected = NULL;
2565 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2567 main_window_cursor_wait(folderview->mainwin);
2569 statusbar_verbosity_set(FALSE);
2570 folder_item_update_freeze();
2571 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2572 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2573 statusbar_verbosity_set(FALSE);
2574 main_window_cursor_normal(folderview->mainwin);
2575 STATUSBAR_POP(folderview->mainwin);
2576 folder_item_update_thaw();
2577 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2579 folderview_sort_folders(folderview,
2580 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2581 NULL, to_folder), new_folder->folder);
2582 folderview_select(folderview, new_folder);
2583 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2585 statusbar_verbosity_set(FALSE);
2586 main_window_cursor_normal(folderview->mainwin);
2587 STATUSBAR_POP(folderview->mainwin);
2588 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2589 folder_item_update_thaw();
2591 case F_MOVE_FAILED_DEST_IS_PARENT:
2592 alertpanel_error(_("Source and destination are the same."));
2594 case F_MOVE_FAILED_DEST_IS_CHILD:
2595 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2596 _("Can't move a folder to one of its children."));
2598 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2599 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2602 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2607 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2610 static gint folderview_clist_compare(GtkCMCList *clist,
2611 gconstpointer ptr1, gconstpointer ptr2)
2613 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2614 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2616 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2618 return item1->order - item2->order;
2621 // if only one folder has an order it comes first
2622 if (item1->order > 0)
2626 if (item2->order > 0)
2632 return (item2->name != NULL);
2636 return g_utf8_collate(item1->name, item2->name);
2639 static void folderview_processing_cb(GtkAction *action, gpointer data)
2641 FolderView *folderview = (FolderView *)data;
2645 if (!folderview->selected) return;
2647 item = folderview_get_selected_item(folderview);
2648 cm_return_if_fail(item != NULL);
2649 cm_return_if_fail(item->folder != NULL);
2651 id = folder_item_get_identifier(item);
2652 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2655 prefs_filtering_open(&item->prefs->processing, title,
2656 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2660 void folderview_set_target_folder_color(gint color_op)
2664 FolderView *folderview;
2666 for (list = folderview_list; list != NULL; list = list->next) {
2667 folderview = (FolderView *)list->data;
2668 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2670 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2671 folderview->color_op;
2677 static gchar *last_smallfont = NULL;
2678 static gchar *last_normalfont = NULL;
2679 static gchar *last_boldfont = NULL;
2680 static gboolean last_derive = 0;
2682 void folderview_reinit_fonts(FolderView *folderview)
2685 g_free(last_smallfont);
2686 last_smallfont = NULL;
2687 g_free(last_normalfont);
2688 last_normalfont = NULL;
2689 g_free(last_boldfont);
2690 last_boldfont = NULL;
2693 void folderview_reflect_prefs(void)
2695 gboolean update_font = FALSE;
2696 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2697 FolderItem *item = folderview_get_selected_item(folderview);
2698 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2699 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2700 gint height = gtk_adjustment_get_value(pos);
2702 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2703 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2704 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2705 last_derive != prefs_common.derive_from_normal_font)
2711 g_free(last_smallfont);
2712 last_smallfont = g_strdup(SMALL_FONT);
2713 g_free(last_normalfont);
2714 last_normalfont = g_strdup(NORMAL_FONT);
2715 g_free(last_boldfont);
2716 last_boldfont = g_strdup(BOLD_FONT);
2717 last_derive = prefs_common.derive_from_normal_font;
2719 normal_style = normal_color_style = bold_style =
2720 bold_color_style = bold_tgtfold_style = NULL;
2722 folderview_init(folderview);
2723 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2724 folderview_column_set_titles(folderview);
2725 folderview_set_all();
2727 g_signal_handlers_block_by_func
2728 (G_OBJECT(folderview->ctree),
2729 G_CALLBACK(folderview_selected), folderview);
2732 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2733 GTK_CMCTREE(folderview->ctree), NULL, item);
2735 folderview_select(folderview, item);
2736 folderview->open_folder = FALSE;
2737 folderview->selected = node;
2740 g_signal_handlers_unblock_by_func
2741 (G_OBJECT(folderview->ctree),
2742 G_CALLBACK(folderview_selected), folderview);
2744 pos = gtk_scrolled_window_get_vadjustment(
2745 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2746 gtk_adjustment_set_value(pos, height);
2747 gtk_adjustment_changed(pos);
2748 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2751 static void drag_state_stop(FolderView *folderview)
2753 if (folderview->drag_timer_id)
2754 g_source_remove(folderview->drag_timer_id);
2755 folderview->drag_timer_id = 0;
2756 folderview->drag_node = NULL;
2759 static gboolean folderview_defer_expand(FolderView *folderview)
2761 if (folderview->drag_node) {
2762 folderview_recollapse_nodes(folderview, folderview->drag_node);
2763 if (folderview->drag_item->collapsed) {
2764 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2765 folderview->nodes_to_recollapse = g_slist_append
2766 (folderview->nodes_to_recollapse, folderview->drag_node);
2769 folderview->drag_item = NULL;
2770 folderview->drag_timer_id = 0;
2774 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2776 /* the idea is that we call drag_state_start() whenever we want expansion to
2777 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2778 * we need to call drag_state_stop() */
2779 drag_state_stop(folderview);
2780 /* request expansion */
2781 if (0 != (folderview->drag_timer_id = g_timeout_add
2782 (prefs_common.hover_timeout,
2783 (GSourceFunc)folderview_defer_expand,
2785 folderview->drag_node = node;
2786 folderview->drag_item = item;
2789 #ifndef GENERIC_UMPC
2790 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2791 FolderView *folderview)
2793 GdkDragContext *context;
2795 cm_return_if_fail(folderview != NULL);
2796 if (folderview->selected == NULL) return;
2797 if (folderview->nodes_to_recollapse)
2798 g_slist_free(folderview->nodes_to_recollapse);
2799 folderview->nodes_to_recollapse = NULL;
2800 context = gtk_drag_begin(widget, folderview->target_list,
2801 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2802 gtk_drag_set_icon_default(context);
2805 static void folderview_drag_data_get(GtkWidget *widget,
2806 GdkDragContext *drag_context,
2807 GtkSelectionData *selection_data,
2810 FolderView *folderview)
2814 gchar *source = NULL;
2815 if (info == TARGET_DUMMY) {
2816 sel = GTK_CMCLIST(folderview->ctree)->selection;
2820 item = gtk_cmctree_node_get_row_data
2821 (GTK_CMCTREE(folderview->ctree),
2822 GTK_CMCTREE_NODE(sel->data));
2824 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2825 gtk_selection_data_set(selection_data,
2826 gtk_selection_data_get_target(selection_data), 8,
2827 source, strlen(source));
2830 g_warning("unknown info %d", info);
2834 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2836 FolderUpdateData *hookdata;
2837 FolderView *folderview;
2841 folderview = (FolderView *) userdata;
2842 cm_return_val_if_fail(hookdata != NULL, FALSE);
2843 cm_return_val_if_fail(folderview != NULL, FALSE);
2845 ctree = folderview->ctree;
2846 cm_return_val_if_fail(ctree != NULL, FALSE);
2848 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2849 folderview_create_folder_node(folderview, hookdata->item);
2850 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2851 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2852 NULL, folder_item_parent(hookdata->item));
2853 folderview_sort_folders(folderview, node, hookdata->folder);
2854 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2855 GtkCMCTreeNode *node;
2857 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2859 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2860 if (folderview->selected == node)
2861 folderview->selected = NULL;
2862 if (folderview->opened == node)
2863 folderview->opened = NULL;
2865 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2866 /* do nothing, it's done by the ADD and REMOVE) */
2867 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2868 folderview_set(folderview);
2873 static gboolean folderview_dnd_scroll_cb(gpointer data)
2875 FolderView *folderview = (FolderView *)data;
2876 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2877 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2878 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2879 gint max = (int)gtk_adjustment_get_upper(pos) -
2880 (int)gtk_adjustment_get_page_size(pos);
2882 if (folderview->scroll_value == 0) {
2883 folderview->scroll_timeout_id = 0;
2887 if (folderview->scroll_value > 0 && new_val > max) {
2889 } else if (folderview->scroll_value < 0 && new_val < 0) {
2892 gtk_adjustment_set_value(pos, new_val);
2897 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2898 GdkDragContext *context,
2902 FolderView *folderview)
2905 FolderItem *item = NULL, *src_item = NULL;
2906 GtkCMCTreeNode *node = NULL;
2907 gboolean acceptable = FALSE;
2908 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2909 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2910 int height = (int)gtk_adjustment_get_page_size(pos);
2911 int total_height = (int)gtk_adjustment_get_upper(pos);
2912 int vpos = (int)gtk_adjustment_get_value(pos);
2913 int offset = prefs_common.show_col_headers ? 24:0;
2916 if (gtk_cmclist_get_selection_info
2917 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2918 GtkWidget *srcwidget;
2920 if (y > height - (48 - offset) && height + vpos < total_height) {
2921 dist = -(height - (48 - offset) - y);
2922 folderview->scroll_value = 1.41f * (1+(dist / 6));
2923 } else if (y < 72 - (24 - offset) && y >= 0) {
2924 dist = 72 - (24 - offset) - y;
2925 folderview->scroll_value = -1.41f * (1+(dist / 6));
2927 folderview->scroll_value = 0;
2929 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2930 folderview->scroll_timeout_id =
2931 g_timeout_add(30, folderview_dnd_scroll_cb,
2935 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2936 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2937 src_item = folderview->summaryview->folder_item;
2939 srcwidget = gtk_drag_get_source_widget(context);
2940 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2941 /* comes from summaryview */
2942 /* we are copying messages, so only accept folder items that are not
2943 the source item, are no root items and can copy messages */
2944 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2945 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2946 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2948 } else if (srcwidget == folderview->ctree) {
2949 /* comes from folderview */
2950 /* we are moving folder items, only accept folders that are not
2951 the source items and can copy messages and create folder items */
2952 if (item && item->folder && src_item && src_item != item &&
2953 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2954 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2955 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2956 || item->folder == src_item->folder))
2959 /* comes from another app */
2960 /* we are adding messages, so only accept folder items that are
2961 no root items and can copy messages */
2962 if (item && item->folder && folder_item_parent(item) != NULL
2963 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2964 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2969 if (acceptable || (src_item && src_item == item))
2970 drag_state_start(folderview, node, item);
2973 g_signal_handlers_block_by_func
2975 G_CALLBACK(folderview_selected), folderview);
2976 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2977 g_signal_handlers_unblock_by_func
2979 G_CALLBACK(folderview_selected), folderview);
2980 gdk_drag_status(context,
2981 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2982 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2984 if (folderview->opened)
2985 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2986 gdk_drag_status(context, 0, time);
2992 static void folderview_drag_leave_cb(GtkWidget *widget,
2993 GdkDragContext *context,
2995 FolderView *folderview)
2997 drag_state_stop(folderview);
2998 folderview->scroll_value = 0;
2999 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3002 static void free_info (gpointer stuff, gpointer data)
3007 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3008 guint time, FolderItem *item)
3011 GSList *msglist = NULL;
3012 list = uri_list_extract_filenames(data);
3013 if (!(item && item->folder && folder_item_parent(item) != NULL
3014 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3016 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3017 debug_print("item doesn't fit\n");
3021 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3022 debug_print("list is empty\n");
3025 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3026 MsgFileInfo *info = NULL;
3028 if (file_is_email((gchar *)tmp->data)) {
3029 info = g_new0(MsgFileInfo, 1);
3030 info->msginfo = NULL;
3031 info->file = (gchar *)tmp->data;
3032 msglist = g_slist_prepend(msglist, info);
3033 debug_print("file is a mail\n");
3035 debug_print("file isn't a mail\n");
3039 msglist = g_slist_reverse(msglist);
3040 folder_item_add_msgs(item, msglist, FALSE);
3041 g_slist_foreach(msglist, free_info, NULL);
3042 g_slist_free(msglist);
3043 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3045 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3047 list_free_strings(list);
3051 static void folderview_drag_received_cb(GtkWidget *widget,
3052 GdkDragContext *drag_context,
3055 GtkSelectionData *data,
3058 FolderView *folderview)
3061 FolderItem *item = NULL, *src_item;
3062 GtkCMCTreeNode *node;
3063 int offset = prefs_common.show_col_headers ? 24:0;
3065 folderview->scroll_value = 0;
3067 if (info == TARGET_DUMMY) {
3068 drag_state_stop(folderview);
3069 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3070 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3071 /* comes from summaryview */
3072 if (gtk_cmclist_get_selection_info
3073 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3076 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3077 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3078 src_item = folderview->summaryview->folder_item;
3080 if (item->no_select) {
3081 alertpanel_error(_("The destination folder can only be used to "
3082 "store subfolders."));
3085 /* re-check (due to acceptable possibly set for folder moves */
3086 if (!(item && item->folder && item->path && !item->no_select &&
3087 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3090 if (item && src_item) {
3091 switch (gdk_drag_context_get_selected_action(drag_context)) {
3092 case GDK_ACTION_COPY:
3093 summary_copy_selected_to(folderview->summaryview, item);
3094 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3096 case GDK_ACTION_MOVE:
3097 case GDK_ACTION_DEFAULT:
3099 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3100 summary_copy_selected_to(folderview->summaryview, item);
3102 summary_move_selected_to(folderview->summaryview, item);
3103 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3106 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3108 /* comes from folderview */
3110 gboolean folder_is_normal = TRUE;
3111 gboolean copy = (GDK_ACTION_COPY ==
3112 gdk_drag_context_get_selected_action(drag_context));
3114 source = (char *)gtk_selection_data_get_data(data) + 17;
3115 if (gtk_cmclist_get_selection_info
3116 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3118 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3121 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3122 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3123 src_item = folder_find_item_from_identifier(source);
3127 src_item->stype == F_NORMAL &&
3128 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3129 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3130 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3131 !folder_has_parent_of_type(src_item, F_TRASH);
3132 if (!item || !src_item || !folder_is_normal) {
3133 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3137 folderview_move_folder(folderview, src_item, item, copy);
3138 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3140 folderview->nodes_to_recollapse = NULL;
3141 } else if (info == TARGET_MAIL_URI_LIST) {
3142 if (gtk_cmclist_get_selection_info
3143 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3146 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3148 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3149 debug_print("no node\n");
3152 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3154 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3155 debug_print("no item\n");
3158 folderview_finish_dnd(gtk_selection_data_get_data(data),
3159 drag_context, time, item);
3163 static void folderview_drag_end_cb(GtkWidget *widget,
3164 GdkDragContext *drag_context,
3165 FolderView *folderview)
3167 drag_state_stop(folderview);
3168 folderview->scroll_value = 0;
3169 g_slist_free(folderview->nodes_to_recollapse);
3170 folderview->nodes_to_recollapse = NULL;
3173 void folderview_register_popup(FolderViewPopup *fpopup)
3177 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3178 FolderView *folderview = folderviews->data;
3179 GtkActionGroup *factory;
3181 factory = create_action_group(folderview, fpopup);
3182 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3184 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3187 void folderview_unregister_popup(FolderViewPopup *fpopup)
3192 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3193 FolderView *folderview = folderviews->data;
3195 g_hash_table_remove(folderview->popups, fpopup->klass);
3197 g_hash_table_remove(folderview_popups, fpopup->klass);
3200 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3202 g_return_if_fail(folderview != NULL);
3203 g_return_if_fail(item != NULL);
3205 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3206 g_return_if_fail(ctree != NULL);
3208 GtkCMCTreeNode *node =
3209 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3210 g_return_if_fail(node != NULL);
3212 gtk_cmctree_remove_node(ctree, node);
3215 void folderview_freeze(FolderView *folderview)
3218 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3221 void folderview_thaw(FolderView *folderview)
3224 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3227 void folderview_grab_focus(FolderView *folderview)
3230 gtk_widget_grab_focus(folderview->ctree);