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"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_handler (GtkAction *action,
170 static void mark_all_read_cb (GtkAction *action,
172 static void mark_all_read_recursive_cb (GtkAction *action,
175 static void folderview_empty_trash_cb (GtkAction *action,
178 static void folderview_send_queue_cb (GtkAction *action,
181 static void folderview_search_cb (GtkAction *action,
183 static void folderview_run_processing_cb(GtkAction *action,
186 static void folderview_property_cb (GtkAction *action,
189 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
190 GdkDragContext *context,
194 FolderView *folderview);
195 static void folderview_drag_leave_cb (GtkWidget *widget,
196 GdkDragContext *context,
198 FolderView *folderview);
199 static void folderview_drag_received_cb (GtkWidget *widget,
200 GdkDragContext *drag_context,
203 GtkSelectionData *data,
206 FolderView *folderview);
208 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
209 FolderView *folderview);
211 static void folderview_drag_data_get (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 GtkSelectionData *selection_data,
216 FolderView *folderview);
217 static void folderview_drag_end_cb (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 FolderView *folderview);
221 static void folderview_create_folder_node (FolderView *folderview,
223 static gboolean folderview_update_folder (gpointer source,
225 static gboolean folderview_update_item_claws (gpointer source,
227 static void folderview_processing_cb(GtkAction *action, gpointer data);
228 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
229 GdkEventButton *event);
231 GHashTable *folderview_popups;
233 static GtkActionEntry folderview_common_popup_entries[] =
235 {"FolderViewPopup", NULL, "FolderViewPopup" },
236 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
237 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
238 {"FolderViewPopup/---", NULL, "---" },
239 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
240 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
241 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
242 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
243 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
244 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
248 GtkTargetEntry folderview_drag_types[] =
250 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
251 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
254 void folderview_initialize(void)
256 FolderViewPopup *fpopup;
258 fpopup = g_new0(FolderViewPopup, 1);
260 fpopup->klass = "common";
261 fpopup->path = "<CommonFolder>";
262 fpopup->entries = folderview_common_popup_entries;
263 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
264 fpopup->set_sensitivity = NULL;
266 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
267 g_hash_table_insert(folderview_popups, "common", fpopup);
270 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
272 FolderViewPopup *fpopup_common;
273 GtkActionGroup *action_group;
275 action_group = cm_menu_create_action_group(
277 fpopup->entries, fpopup->n_entries,
278 (gpointer)folderview);
280 if (fpopup->toggle_entries)
281 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
282 fpopup->n_toggle_entries,
283 (gpointer)folderview);
284 if (fpopup->radio_entries)
285 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
286 fpopup->n_radio_entries, fpopup->radio_default,
287 G_CALLBACK(fpopup->radio_callback),
288 (gpointer)folderview);
290 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
291 if (fpopup_common != fpopup) {
292 gtk_action_group_add_actions(action_group, fpopup_common->entries,
293 fpopup_common->n_entries,
294 (gpointer)folderview);
295 if (fpopup_common->toggle_entries)
296 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
297 fpopup_common->n_toggle_entries,
298 (gpointer)folderview);
299 if (fpopup_common->radio_entries)
300 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
301 fpopup_common->n_radio_entries, fpopup_common->radio_default,
302 G_CALLBACK(fpopup_common->radio_callback),
303 (gpointer)folderview);
309 static void create_action_groups(gpointer key, gpointer value, gpointer data)
311 FolderView *folderview = data;
312 FolderViewPopup *fpopup = value;
313 GtkActionGroup *group;
315 group = create_action_group(folderview, fpopup);
316 g_hash_table_insert(folderview->popups, fpopup->klass, group);
319 static void folderview_column_set_titles(FolderView *folderview)
321 GtkWidget *ctree = folderview->ctree;
322 GtkWidget *label_folder;
323 GtkWidget *label_new;
324 GtkWidget *label_unread;
325 GtkWidget *label_total;
326 GtkWidget *hbox_folder;
328 GtkWidget *hbox_unread;
329 GtkWidget *hbox_total;
330 gint *col_pos = folderview->col_pos;
332 debug_print("setting titles...\n");
333 gtk_widget_realize(folderview->ctree);
334 gtk_widget_show_all(folderview->scrolledwin);
336 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
337 * instead text (text overflows making them unreadable and ugly) */
338 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
340 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
342 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
345 label_folder = gtk_label_new(_("Folder"));
346 label_new = gtk_image_new_from_pixbuf(newxpm);
347 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
348 label_total = gtk_image_new_from_pixbuf(readxpm);
350 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
352 hbox_folder = gtk_hbox_new(FALSE, 4);
353 hbox_new = gtk_hbox_new(FALSE, 4);
354 hbox_unread = gtk_hbox_new(FALSE, 4);
355 hbox_total = gtk_hbox_new(FALSE, 4);
358 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
359 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
360 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
361 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
362 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
363 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
364 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
365 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
367 gtk_widget_show_all(hbox_folder);
368 gtk_widget_show_all(hbox_new);
369 gtk_widget_show_all(hbox_unread);
370 gtk_widget_show_all(hbox_total);
373 gtk_widget_set_size_request(hbox_new, -1, 20);
374 gtk_widget_set_size_request(hbox_unread, -1, 20);
375 gtk_widget_set_size_request(hbox_total, -1, 20);
378 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
379 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
380 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
381 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
387 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
388 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
389 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
392 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
394 FolderView *folderview = (FolderView *)data;
395 GdkEventButton event;
396 if (folderview_get_selected_item(folderview) == NULL)
400 event.time = gtk_get_current_event_time();
402 folderview_set_sens_and_popup_menu(folderview, -1,
409 static GtkWidget *folderview_ctree_create(FolderView *folderview)
413 FolderColumnState *col_state;
414 FolderColumnType type;
415 gchar *titles[N_FOLDER_COLS];
417 GtkWidget *scrolledwin = folderview->scrolledwin;
419 debug_print("creating tree...\n");
420 memset(titles, 0, sizeof(titles));
422 col_state = prefs_folder_column_get_config();
423 memset(titles, 0, sizeof(titles));
425 col_pos = folderview->col_pos;
427 for (i = 0; i < N_FOLDER_COLS; i++) {
428 folderview->col_state[i] = col_state[i];
429 type = col_state[i].type;
433 titles[col_pos[F_COL_FOLDER]] = _("Folder");
434 titles[col_pos[F_COL_NEW]] = _("New");
435 titles[col_pos[F_COL_UNREAD]] = _("Unread");
436 /* TRANSLATORS: This in Number sign in American style */
437 titles[col_pos[F_COL_TOTAL]] = _("#");
439 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
442 if (prefs_common.show_col_headers == FALSE)
443 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
446 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
447 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
449 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
450 col_pos[F_COL_UNREAD],
452 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
453 col_pos[F_COL_TOTAL],
455 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
456 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
457 GTK_CMCTREE_EXPANDER_TRIANGLE);
459 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
460 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
462 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
463 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
465 /* don't let title buttons take key focus */
466 for (i = 0; i < N_FOLDER_COLS; i++) {
467 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
468 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
469 prefs_common.folder_col_size[i]);
470 gtk_cmclist_set_column_visibility
471 (GTK_CMCLIST(ctree), i, col_state[i].visible);
474 g_signal_connect(G_OBJECT(ctree), "key_press_event",
475 G_CALLBACK(folderview_key_pressed),
477 g_signal_connect(G_OBJECT(ctree), "button_press_event",
478 G_CALLBACK(folderview_button_pressed),
480 g_signal_connect(G_OBJECT(ctree), "popup-menu",
481 G_CALLBACK(folderview_popup_menu), folderview);
482 g_signal_connect(G_OBJECT(ctree), "button_release_event",
483 G_CALLBACK(folderview_button_released),
485 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
486 G_CALLBACK(folderview_selected), folderview);
488 /* drag-n-dropping folders on maemo is impractical as this
489 * opens the folder almost everytime */
490 g_signal_connect(G_OBJECT(ctree), "start_drag",
491 G_CALLBACK(folderview_start_drag), folderview);
493 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
494 G_CALLBACK(folderview_drag_data_get),
497 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
498 G_CALLBACK(folderview_tree_expanded),
500 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
501 G_CALLBACK(folderview_tree_collapsed),
504 g_signal_connect(G_OBJECT(ctree), "resize_column",
505 G_CALLBACK(folderview_col_resized),
509 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
510 folderview_drag_types, 2,
511 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
512 g_signal_connect(G_OBJECT(ctree), "drag_motion",
513 G_CALLBACK(folderview_drag_motion_cb),
515 g_signal_connect(G_OBJECT(ctree), "drag_leave",
516 G_CALLBACK(folderview_drag_leave_cb),
518 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
519 G_CALLBACK(folderview_drag_received_cb),
521 g_signal_connect(G_OBJECT(ctree), "drag_end",
522 G_CALLBACK(folderview_drag_end_cb),
525 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
530 void folderview_set_column_order(FolderView *folderview)
532 GtkWidget *ctree = folderview->ctree;
533 FolderItem *item = folderview_get_selected_item(folderview);
534 FolderItem *sel_item = NULL, *op_item = NULL;
535 GtkWidget *scrolledwin = folderview->scrolledwin;
537 if (folderview->drag_timer_id != 0) {
538 g_source_remove(folderview->drag_timer_id);
539 folderview->drag_timer_id = 0;
541 if (folderview->deferred_refresh_id != 0) {
542 g_source_remove(folderview->deferred_refresh_id);
543 folderview->deferred_refresh_id = 0;
545 if (folderview->scroll_timeout_id != 0) {
546 g_source_remove(folderview->scroll_timeout_id);
547 folderview->scroll_timeout_id = 0;
549 if (folderview->postpone_select_id != 0) {
550 g_source_remove(folderview->postpone_select_id);
551 folderview->postpone_select_id = 0;
554 if (folderview->selected)
555 sel_item = folderview_get_selected_item(folderview);
556 if (folderview->opened)
557 op_item = folderview_get_opened_item(folderview);
559 debug_print("recreating tree...\n");
560 gtk_widget_destroy(folderview->ctree);
563 folderview->ctree = ctree = folderview_ctree_create(folderview);
564 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
565 GTK_CMCLIST(ctree)->hadjustment);
566 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
567 GTK_CMCLIST(ctree)->vadjustment);
568 gtk_widget_show(ctree);
571 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
573 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
575 folderview_set(folderview);
576 folderview_column_set_titles(folderview);
578 folderview_select(folderview,item);
581 FolderView *folderview_create(void)
583 FolderView *folderview;
584 GtkWidget *scrolledwin;
587 debug_print("Creating folder view...\n");
588 folderview = g_new0(FolderView, 1);
590 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
591 gtk_scrolled_window_set_policy
592 (GTK_SCROLLED_WINDOW(scrolledwin),
593 GTK_POLICY_AUTOMATIC,
594 prefs_common.folderview_vscrollbar_policy);
595 gtk_widget_set_size_request(scrolledwin,
596 prefs_common.folderview_width,
597 prefs_common.folderview_height);
599 folderview->scrolledwin = scrolledwin;
600 ctree = folderview_ctree_create(folderview);
602 /* create popup factories */
603 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
604 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
606 folderview->ctree = ctree;
608 folderview->folder_update_callback_id =
609 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
610 folderview->folder_item_update_callback_id =
611 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
613 gtk_widget_show_all(scrolledwin);
615 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
616 folderview_list = g_list_append(folderview_list, folderview);
618 folderview->drag_timer_id = 0;
619 folderview->deferred_refresh_id = 0;
620 folderview->scroll_timeout_id = 0;
621 folderview->postpone_select_id = 0;
626 void folderview_init(FolderView *folderview)
628 GtkWidget *ctree = folderview->ctree;
630 PangoFontDescription *normal_font;
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
670 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
671 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
672 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
673 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
674 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
675 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
676 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
677 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
679 normal_font = pango_font_description_from_string(NORMAL_FONT);
681 gtk_widget_modify_font(ctree, normal_font);
682 pango_font_description_free(normal_font);
684 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
687 PangoFontDescription *font_desc;
688 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
689 font_desc = pango_font_description_from_string(NORMAL_FONT);
691 if (normal_style->font_desc)
692 pango_font_description_free
693 (normal_style->font_desc);
694 normal_style->font_desc = font_desc;
696 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
697 normal_color_style = gtk_style_copy(normal_style);
698 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
702 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
703 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
704 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
705 PangoFontDescription *font_desc;
706 font_desc = pango_font_description_from_string(NORMAL_FONT);
708 pango_font_description_free(bold_style->font_desc);
709 bold_style->font_desc = font_desc;
711 pango_font_description_set_weight
712 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
714 PangoFontDescription *font_desc;
715 font_desc = pango_font_description_from_string(BOLD_FONT);
717 if (bold_style->font_desc)
718 pango_font_description_free
719 (bold_style->font_desc);
720 bold_style->font_desc = font_desc;
723 bold_color_style = gtk_style_copy(bold_style);
724 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
726 bold_tgtfold_style = gtk_style_copy(bold_style);
727 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
731 static gboolean folderview_defer_set(gpointer data)
733 FolderView *folderview = (FolderView *)data;
734 MainWindow *mainwin = folderview->mainwin;
738 if (mainwin->lock_count)
741 debug_print("doing deferred folderview_set now\n");
742 folderview_set(folderview);
744 folderview->deferred_refresh_id = 0;
748 void folderview_set(FolderView *folderview)
750 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
751 MainWindow *mainwin = folderview->mainwin;
752 FolderItem *sel_item = NULL, *op_item = NULL;
757 if (mainwin->lock_count) {
758 if (folderview->deferred_refresh_id == 0)
759 folderview->deferred_refresh_id =
760 g_timeout_add(500, folderview_defer_set, folderview);
761 debug_print("deferred folderview_set\n");
766 debug_print("Setting folder info...\n");
767 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
769 main_window_cursor_wait(mainwin);
771 if (folderview->selected)
772 sel_item = folderview_get_selected_item(folderview);
773 if (folderview->opened)
774 op_item = folderview_get_opened_item(folderview);
776 folderview->selected = NULL;
777 folderview->opened = NULL;
779 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
780 gtk_cmclist_clear(GTK_CMCLIST(ctree));
782 folderview_set_folders(folderview);
785 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
787 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
789 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
790 main_window_cursor_normal(mainwin);
791 STATUSBAR_POP(mainwin);
795 void folderview_set_all(void)
799 for (list = folderview_list; list != NULL; list = list->next)
800 folderview_set((FolderView *)list->data);
803 void folderview_select(FolderView *folderview, FolderItem *item)
805 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
806 GtkCMCTreeNode *node;
807 GtkCMCTreeNode *old_selected = folderview->selected;
811 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
812 if (node) folderview_select_node(folderview, node);
814 if (old_selected != node)
815 folder_update_op_count();
818 static void mark_all_read_cb(GtkAction *action, gpointer data)
820 mark_all_read_handler(action, data, FALSE);
823 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
825 mark_all_read_handler(action, data, TRUE);
828 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
830 FolderView *folderview = (FolderView *)data;
835 item = folderview_get_selected_item(folderview);
839 message = recursive? _("Do you really want to mark all mails in this "
840 "folder and its sub-folders as read?") :
841 _("Do you really want to mark all mails in this "
843 if (folderview->summaryview->folder_item != item &&
844 prefs_common.ask_mark_all_read) {
845 val = alertpanel_full(_("Mark all as read"),
846 message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
847 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
849 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
851 else if (val & G_ALERTDISABLE)
852 prefs_common.ask_mark_all_read = FALSE;
855 folder_item_update_freeze();
856 if (folderview->summaryview->folder_item != item && !recursive)
857 summary_lock(folderview->summaryview);
859 summary_freeze(folderview->summaryview);
862 folderutils_mark_all_read_recursive(item);
864 folderutils_mark_all_read(item);
866 if (folderview->summaryview->folder_item != item && !recursive)
867 summary_unlock(folderview->summaryview);
869 summary_thaw(folderview->summaryview);
870 folder_item_update_thaw();
873 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
875 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
877 cm_return_if_fail(node != NULL);
879 if (folderview->open_folder) {
883 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
884 gtkut_ctree_expand_parent_all(ctree, node);
886 folderview->open_folder = TRUE;
887 gtkut_ctree_set_focus_row(ctree, node);
888 gtk_cmctree_select(ctree, node);
889 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
890 if ((folderview->summaryview->folder_item &&
891 folderview->summaryview->folder_item->total_msgs > 0) ||
892 prefs_common.layout_mode == SMALL_LAYOUT)
893 summary_grab_focus(folderview->summaryview);
895 gtk_widget_grab_focus(folderview->ctree);
898 void folderview_unselect(FolderView *folderview)
900 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
902 (GTK_CMCTREE(folderview->ctree), folderview->opened);
904 folderview->selected = folderview->opened = NULL;
907 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
908 GtkCMCTreeNode *node,
914 node = gtkut_ctree_node_next(ctree, node);
916 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
918 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
919 item = gtk_cmctree_node_get_row_data(ctree, node);
922 if (item->stype == F_TRASH || item->stype == F_DRAFT)
926 if(item->unread_msgs > 0)
930 if(item->new_msgs > 0)
934 if(item->marked_msgs > 0)
943 void folderview_select_next_with_flag(FolderView *folderview,
947 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
948 GtkCMCTreeNode *node = NULL;
949 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
950 gboolean last_open = prefs_common.always_show_msg;
954 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
957 prefs_common.summary_select_prio[0] = ACTION_NEW;
960 prefs_common.summary_select_prio[0] = ACTION_MARKED;
963 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
965 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
967 folderview_select_node(folderview, node);
971 if (!folderview->opened ||
972 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
976 /* search again from the first node */
977 node = folderview_find_next_with_flag(ctree, NULL, flag);
979 folderview_select_node(folderview, node);
982 prefs_common.summary_select_prio[0] = last_summary_select_prio;
983 prefs_common.always_show_msg = last_open;
986 FolderItem *folderview_get_selected_item(FolderView *folderview)
988 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
990 if (!folderview->selected) return NULL;
991 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
994 FolderItem *folderview_get_opened_item(FolderView *folderview)
996 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
998 if (!folderview->opened) return NULL;
999 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1002 static void folderview_set_folders(FolderView *folderview)
1005 list = folder_get_list();
1007 for (; list != NULL; list = list->next) {
1008 folderview_append_folder(folderview, FOLDER(list->data));
1012 static gchar *get_scan_str(FolderItem *item)
1015 return g_strdup_printf(_("Scanning folder %s/%s..."),
1016 item->folder->name, item->path);
1018 return g_strdup_printf(_("Scanning folder %s..."),
1019 item->folder->name);
1021 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1025 for (list = folderview_list; list != NULL; list = list->next) {
1026 FolderView *folderview = (FolderView *)list->data;
1027 MainWindow *mainwin = folderview->mainwin;
1028 gchar *str = get_scan_str(item);
1030 STATUSBAR_PUSH(mainwin, str);
1031 STATUSBAR_POP(mainwin);
1036 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1039 MainWindow *mainwin = mainwindow_get_mainwindow();
1040 FolderView *folderview = NULL;
1041 GtkAdjustment *pos = NULL;
1044 cm_return_if_fail(folder != NULL);
1046 if (!folder->klass->scan_tree) return;
1049 alertpanel_full(_("Rebuild folder tree"),
1050 _("Rebuilding the folder tree will remove "
1051 "local caches. Do you want to continue?"),
1052 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1053 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1054 != G_ALERTALTERNATE) {
1060 window = label_window_create(_("Rebuilding folder tree..."));
1062 window = label_window_create(_("Scanning folder tree..."));
1065 folderview = mainwin->folderview;
1068 pos = gtk_scrolled_window_get_vadjustment(
1069 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1070 height = gtk_adjustment_get_value(pos);
1073 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1074 folder_scan_tree(folder, rebuild);
1075 folder_set_ui_func(folder, NULL, NULL);
1077 folderview_set_all();
1080 pos = gtk_scrolled_window_get_vadjustment(
1081 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1082 gtk_adjustment_set_value(pos, height);
1083 gtk_adjustment_changed(pos);
1085 label_window_destroy(window);
1089 /** folderview_check_new()
1090 * Scan and update the folder and return the
1091 * count the number of new messages since last check.
1092 * \param folder the folder to check for new messages
1093 * \return the number of new messages since last check
1095 gint folderview_check_new(Folder *folder)
1099 FolderView *folderview;
1101 GtkCMCTreeNode *node;
1103 gint former_new_msgs = 0;
1104 gint former_new = 0, former_unread = 0, former_total;
1106 for (list = folderview_list; list != NULL; list = list->next) {
1107 folderview = (FolderView *)list->data;
1108 ctree = GTK_CMCTREE(folderview->ctree);
1109 folderview->scanning_folder = folder;
1111 main_window_lock(folderview->mainwin);
1113 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1114 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1116 item = gtk_cmctree_node_get_row_data(ctree, node);
1117 if (!item || !item->path || !item->folder) continue;
1118 if (item->no_select) continue;
1119 if (folder && folder != item->folder) continue;
1120 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1121 if (!item->prefs->newmailcheck) continue;
1122 if (item->processing_pending == TRUE) {
1123 debug_print("skipping %s, processing pending\n",
1124 item->path ? item->path : item->name);
1127 if (item->scanning != ITEM_NOT_SCANNING) {
1128 debug_print("skipping %s, scanning\n",
1129 item->path ? item->path : item->name);
1133 str = get_scan_str(item);
1135 STATUSBAR_PUSH(folderview->mainwin, str);
1139 folderview_scan_tree_func(item->folder, item, NULL);
1140 former_new = item->new_msgs;
1141 former_unread = item->unread_msgs;
1142 former_total = item->total_msgs;
1144 if (item->folder->klass->scan_required &&
1145 (item->folder->klass->scan_required(item->folder, item) ||
1146 item->folder->inbox == item ||
1147 item->opened == TRUE ||
1148 item->processing_pending == TRUE)) {
1149 if (folder_item_scan(item) < 0) {
1151 summaryview_unlock(folderview->summaryview, item);
1152 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1153 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1154 item->path ? item->path:item->name);
1155 STATUSBAR_POP(folderview->mainwin);
1157 } else if (!FOLDER_IS_LOCAL(folder)) {
1158 STATUSBAR_POP(folderview->mainwin);
1163 } else if (!item->folder->klass->scan_required) {
1164 if (folder_item_scan(item) < 0) {
1165 summaryview_unlock(folderview->summaryview, item);
1166 if (folder && !FOLDER_IS_LOCAL(folder)) {
1167 STATUSBAR_POP(folderview->mainwin);
1172 if (former_new != item->new_msgs ||
1173 former_unread != item->unread_msgs ||
1174 former_total != item->total_msgs)
1175 folderview_update_node(folderview, node);
1177 new_msgs += item->new_msgs;
1178 former_new_msgs += former_new;
1179 STATUSBAR_POP(folderview->mainwin);
1181 folderview->scanning_folder = NULL;
1182 main_window_unlock(folderview->mainwin);
1186 folder_write_list();
1187 /* Number of new messages since last check is the just the difference
1188 * between former_new_msgs and new_msgs. If new_msgs is less than
1189 * former_new_msgs, that would mean another session accessed the folder
1190 * and the result is not well defined.
1192 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1196 void folderview_check_new_all(void)
1200 FolderView *folderview;
1202 folderview = (FolderView *)folderview_list->data;
1205 main_window_lock(folderview->mainwin);
1206 window = label_window_create
1207 (_("Checking for new messages in all folders..."));
1209 list = folder_get_list();
1210 for (; list != NULL; list = list->next) {
1211 Folder *folder = list->data;
1213 folderview_check_new(folder);
1216 folder_write_list();
1217 folderview_set_all();
1219 label_window_destroy(window);
1220 main_window_unlock(folderview->mainwin);
1224 static gboolean folderview_have_children_sub(FolderView *folderview,
1230 if (!item || !item->folder || !item->folder->node)
1233 node = item->folder->node;
1235 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1236 node = node->children;
1238 if (in_sub && item->total_msgs > 0) {
1242 while (node != NULL) {
1243 if (node && node->data) {
1244 FolderItem *next_item = (FolderItem*) node->data;
1246 if (folderview_have_children_sub(folderview,
1255 static gboolean folderview_have_children(FolderView *folderview,
1258 return folderview_have_children_sub(folderview, item, FALSE);
1261 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1267 if (!item || !item->folder || !item->folder->node)
1270 node = item->folder->node;
1272 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1273 node = node->children;
1276 (item->new_msgs > 0 ||
1277 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1281 while (node != NULL) {
1282 if (node && node->data) {
1283 FolderItem *next_item = (FolderItem*) node->data;
1285 if (folderview_have_new_children_sub(folderview,
1294 static gboolean folderview_have_new_children(FolderView *folderview,
1297 return folderview_have_new_children_sub(folderview, item, FALSE);
1300 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1306 if (!item || !item->folder || !item->folder->node)
1309 node = item->folder->node;
1311 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1312 node = node->children;
1315 (item->unread_msgs > 0 ||
1316 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1320 while (node != NULL) {
1321 if (node && node->data) {
1322 FolderItem *next_item = (FolderItem*) node->data;
1324 if (folderview_have_unread_children_sub(folderview,
1334 static gboolean folderview_have_unread_children(FolderView *folderview,
1337 return folderview_have_unread_children_sub(folderview, item, FALSE);
1340 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1346 if (!item || !item->folder || !item->folder->node)
1349 node = item->folder->node;
1351 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1352 node = node->children;
1354 if (in_sub && item->search_match){
1358 while (node != NULL) {
1359 if (node && node->data) {
1360 FolderItem *next_item = (FolderItem*) node->data;
1362 if (folderview_have_matching_children_sub(folderview,
1372 static gboolean folderview_have_matching_children(FolderView *folderview,
1375 return folderview_have_matching_children_sub(folderview, item, FALSE);
1378 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1384 if (!item || !item->folder || !item->folder->node)
1387 node = item->folder->node;
1389 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1390 node = node->children;
1392 if (item->marked_msgs != 0) {
1396 while (node != NULL) {
1397 if (node && node->data) {
1398 FolderItem *next_item = (FolderItem*) node->data;
1400 if (folderview_have_marked_children_sub(folderview,
1409 static gboolean folderview_have_marked_children(FolderView *folderview,
1412 return folderview_have_marked_children_sub(folderview, item, FALSE);
1415 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1417 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1418 GtkStyle *style = NULL;
1419 GtkStyle *color_style = NULL;
1421 GdkPixbuf *xpm, *openxpm;
1422 static GdkPixbuf *searchicon;
1423 gboolean mark = FALSE;
1426 gboolean add_unread_mark;
1427 gboolean add_sub_match_mark;
1428 gboolean use_bold, use_color;
1429 gint *col_pos = folderview->col_pos;
1430 SpecialFolderItemType stype;
1432 item = gtk_cmctree_node_get_row_data(ctree, node);
1433 cm_return_if_fail(item != NULL);
1435 if (!GTK_CMCTREE_ROW(node)->expanded)
1436 mark = folderview_have_marked_children(folderview, item);
1438 mark = (item->marked_msgs != 0);
1440 stype = item->stype;
1441 if (stype == F_NORMAL) {
1442 if (folder_has_parent_of_type(item, F_TRASH))
1444 else if (folder_has_parent_of_type(item, F_DRAFT))
1446 else if (folder_has_parent_of_type(item, F_OUTBOX))
1448 else if (folder_has_parent_of_type(item, F_QUEUE))
1453 if (item->hide_read_msgs || item->hide_read_threads) {
1454 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1455 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1457 xpm = mark?m_inboxxpm:inboxxpm;
1458 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1462 if (item->hide_read_msgs || item->hide_read_threads) {
1463 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1464 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1466 xpm = mark?m_outboxxpm:outboxxpm;
1467 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1471 if (item->hide_read_msgs || item->hide_read_threads) {
1472 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1473 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1475 xpm = mark?m_queuexpm:queuexpm;
1476 openxpm = mark?m_queueopenxpm:queueopenxpm;
1480 if (item->hide_read_msgs || item->hide_read_threads) {
1481 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1482 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1484 xpm = mark?m_trashxpm:trashxpm;
1485 openxpm = mark?m_trashopenxpm:trashopenxpm;
1489 xpm = mark?m_draftsxpm:draftsxpm;
1490 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1493 if (item->hide_read_msgs || item->hide_read_threads) {
1494 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1495 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1497 xpm = mark?m_folderxpm:folderxpm;
1498 openxpm = mark?m_folderopenxpm:folderopenxpm;
1502 if (item->no_select) {
1503 xpm = openxpm = noselectxpm;
1506 name = folder_item_get_name(item);
1508 if (!GTK_CMCTREE_ROW(node)->expanded) {
1509 add_unread_mark = folderview_have_unread_children(
1511 add_sub_match_mark = folderview_have_matching_children(
1514 add_unread_mark = FALSE;
1515 add_sub_match_mark = FALSE;
1518 if (item->search_match) {
1520 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1523 xpm = openxpm = searchicon;
1527 if (prefs_common.display_folder_unread) {
1528 if (folder_has_parent_of_type(item, F_QUEUE)) {
1529 /* only total_msgs matters here */
1530 if (item->total_msgs > 0) {
1531 /* show total number (should be equal to the unread number)
1533 str = g_strdup_printf("%s (%d%s%s)",
1534 name, item->total_msgs,
1535 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1536 (item->unreadmarked_msgs > 0) ? "!" : "");
1539 if (prefs_common.display_folder_unread == 1) {
1540 if (item->unread_msgs > 0) {
1541 /* show unread number and signs */
1542 str = g_strdup_printf("%s (%d%s%s)",
1543 name, item->unread_msgs,
1544 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1545 (item->unreadmarked_msgs > 0) ? "!" : "");
1548 if (item->total_msgs > 0) {
1549 /* show unread number, total number and signs if any */
1550 str = g_strdup_printf("%s (%d/%d%s%s)",
1551 name, item->unread_msgs, item->total_msgs,
1552 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1553 (item->unreadmarked_msgs > 0) ? "!" : "");
1557 if ((str == NULL) &&
1558 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1559 /* no unread/total numbers, but at least one sign */
1560 str = g_strdup_printf("%s (%s%s)",
1562 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1563 (item->unreadmarked_msgs > 0) ? "!" : "");
1567 /* last fallback, folder name only or with +! sign */
1568 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1569 str = g_strdup_printf("%s%s",
1571 } else if (item->unreadmarked_msgs > 0) {
1572 str = g_strdup_printf("%s%s",
1574 } else if (add_sub_match_mark) {
1575 str = g_strdup_printf("%s%s",
1578 str = g_strdup_printf("%s", name);
1581 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1583 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1587 if (!folder_item_parent(item)) {
1588 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1589 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1590 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1592 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1593 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1594 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1597 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1598 folder_has_parent_of_type(item, F_TRASH)) {
1599 use_bold = use_color = FALSE;
1600 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1601 GSList *list = folder_item_get_msg_list(item);
1603 use_bold = use_color = FALSE;
1604 for (cur = list; cur; cur = cur->next) {
1605 MsgInfo *msginfo = (MsgInfo *)cur->data;
1606 if (!MSG_IS_DELETED(msginfo->flags)) {
1607 /* highlight queue folder if there are any messages */
1608 use_bold = use_color = TRUE;
1612 if (!GTK_CMCTREE_ROW(node)->expanded &&
1613 use_bold == FALSE &&
1614 folderview_have_children(folderview, item))
1615 use_bold = use_color = TRUE;
1616 procmsg_msg_list_free(list);
1618 /* if unread messages exist, print with bold font */
1619 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1621 /* if new messages exist, print with colored letter */
1623 (item->new_msgs > 0) ||
1625 folderview_have_new_children(folderview, item));
1628 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1633 if (item->prefs->color > 0 && !use_color) {
1634 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1635 color_style = gtk_style_copy(bold_style);
1636 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1637 style = color_style;
1638 } else if (use_color) {
1639 style = bold_color_style;
1642 if (item->op_count > 0) {
1643 style = bold_tgtfold_style;
1645 } else if (use_color) {
1646 style = normal_color_style;
1647 gtk_cmctree_node_set_foreground(ctree, node,
1648 &folderview->color_new);
1649 } else if (item->op_count > 0) {
1650 style = bold_tgtfold_style;
1651 } else if (item->prefs->color > 0) {
1653 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1654 color_style = gtk_style_copy(normal_style);
1655 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1656 style = color_style;
1658 style = normal_style;
1661 gtk_cmctree_node_set_row_style(ctree, node, style);
1663 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1664 folderview_update_node(folderview, node);
1667 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1670 FolderView *folderview;
1672 GtkCMCTreeNode *node;
1674 cm_return_if_fail(item != NULL);
1676 for (list = folderview_list; list != NULL; list = list->next) {
1677 folderview = (FolderView *)list->data;
1678 ctree = GTK_CMCTREE(folderview->ctree);
1680 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1681 if (node && item->search_match != matches) {
1682 item->search_match = matches;
1683 folderview_update_node(folderview, node);
1688 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1690 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1691 FolderView *folderview = (FolderView *)data;
1693 GtkCMCTreeNode *node;
1694 cm_return_val_if_fail(update_info != NULL, TRUE);
1695 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1696 cm_return_val_if_fail(folderview != NULL, FALSE);
1698 ctree = GTK_CMCTREE(folderview->ctree);
1700 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1703 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1704 folderview_update_node(folderview, node);
1706 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1707 update_info->item == folderview->summaryview->folder_item &&
1708 update_info->item != NULL)
1709 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1710 summary_show(folderview->summaryview, update_info->item);
1716 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1717 GNode *gnode, GtkCMCTreeNode *cnode,
1720 FolderView *folderview = (FolderView *)data;
1721 FolderItem *item = FOLDER_ITEM(gnode->data);
1723 cm_return_val_if_fail(item != NULL, FALSE);
1725 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1726 folderview_update_node(folderview, cnode);
1731 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1734 FolderView *folderview = (FolderView *)data;
1737 if (GTK_CMCTREE_ROW(node)->children) {
1738 item = gtk_cmctree_node_get_row_data(ctree, node);
1739 cm_return_if_fail(item != NULL);
1741 if (!item->collapsed)
1742 gtk_cmctree_expand(ctree, node);
1744 folderview_update_node(folderview, node);
1748 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1749 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1752 GtkCMCTreeNode *node, *parent, *sibling;
1754 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1756 g_warning("%s not found.", item->path);
1758 parent = GTK_CMCTREE_ROW(node)->parent;
1759 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1760 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1762 sibling = GTK_CMCTREE_ROW(parent)->children;
1766 tmp = gtk_cmctree_node_get_row_data
1768 if (tmp && tmp->stype != F_NORMAL)
1769 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1773 if (node != sibling)
1774 gtk_cmctree_move(ctree, node, parent, sibling);
1781 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1784 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1785 GtkCMCTreeNode *prev = NULL;
1787 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1788 gtk_sctree_sort_recursive(ctree, root);
1789 if (root && GTK_CMCTREE_ROW(root)->parent) {
1790 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1793 set_special_folder(ctree, folder->inbox, root, &prev);
1794 set_special_folder(ctree, folder->outbox, root, &prev);
1795 set_special_folder(ctree, folder->draft, root, &prev);
1796 set_special_folder(ctree, folder->queue, root, &prev);
1797 set_special_folder(ctree, folder->trash, root, &prev);
1798 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1801 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1803 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1804 GtkCMCTreeNode *root;
1806 cm_return_if_fail(folder != NULL);
1808 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1809 folderview_gnode_func, folderview);
1810 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1812 folderview_sort_folders(folderview, root, folder);
1815 /* callback functions */
1816 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1817 GdkEventButton *event)
1821 FolderViewPopup *fpopup;
1822 GtkActionGroup *action_group;
1824 FolderItem *special_trash = NULL, *special_queue = NULL;
1826 GtkUIManager *ui_manager = gtk_ui_manager_new();
1828 if (folderview->ui_manager)
1829 g_object_unref(folderview->ui_manager);
1831 folderview->ui_manager = ui_manager;
1832 item = folderview_get_selected_item(folderview);
1834 cm_return_if_fail(item != NULL);
1835 cm_return_if_fail(item->folder != NULL);
1836 folder = item->folder;
1838 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1841 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1843 fpopup = g_hash_table_lookup(folderview_popups, "common");
1844 action_group = g_hash_table_lookup(folderview->popups, "common");
1847 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1848 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1851 if (fpopup->add_menuitems)
1852 fpopup->add_menuitems(ui_manager, item);
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1855 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1856 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1857 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1858 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1859 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1860 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1862 if (fpopup->set_sensitivity != NULL)
1863 fpopup->set_sensitivity(ui_manager, item);
1865 if (NULL != (ac = account_find_from_item(item))) {
1866 special_trash = account_get_special_folder(ac, F_TRASH);
1867 special_queue = account_get_special_folder(ac, F_QUEUE);
1870 if ((item == folder->trash || item == special_trash
1871 || folder_has_parent_of_type(item, F_TRASH))) {
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1873 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1876 if ((item == folder->queue || item == special_queue
1877 || folder_has_parent_of_type(item, F_QUEUE))) {
1878 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1879 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1882 #define SET_SENS(name, sens) \
1883 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1885 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1886 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1887 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1888 folderview->selected == folderview->opened);
1889 SET_SENS("FolderViewPopup/Properties", TRUE);
1891 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1892 item->total_msgs >= 1 && !item->processing_pending);
1893 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1894 !item->no_select && !item->processing_pending);
1896 if (item == folder->trash || item == special_trash
1897 || folder_has_parent_of_type(item, F_TRASH)) {
1898 GSList *msglist = folder_item_get_msg_list(item);
1899 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1900 procmsg_msg_list_free(msglist);
1902 if (item == folder->queue || item == special_queue
1903 || folder_has_parent_of_type(item, F_QUEUE)) {
1904 GSList *msglist = folder_item_get_msg_list(item);
1905 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1906 procmsg_msg_list_free(msglist);
1910 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1911 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1912 g_signal_connect(G_OBJECT(popup), "selection_done",
1913 G_CALLBACK(folderview_popup_close),
1915 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1916 event->button, event->time);
1919 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1920 FolderView *folderview)
1922 GtkCMCList *clist = GTK_CMCLIST(ctree);
1923 gint prev_row = -1, row = -1, column = -1;
1925 if (!event) return FALSE;
1927 if (event->button == 1 || event->button == 2) {
1928 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1929 folderview->open_folder = TRUE;
1931 if (event->type == GDK_2BUTTON_PRESS) {
1932 if (clist->selection) {
1933 GtkCMCTreeNode *node;
1935 node = GTK_CMCTREE_NODE(clist->selection->data);
1937 gtk_cmctree_toggle_expansion(
1940 folderview->open_folder = FALSE;
1947 if (event->button == 2 || event->button == 3) {
1949 if (clist->selection) {
1950 GtkCMCTreeNode *node;
1952 node = GTK_CMCTREE_NODE(clist->selection->data);
1954 prev_row = gtkut_ctree_get_nth_from_node
1955 (GTK_CMCTREE(ctree), node);
1958 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1961 if (prev_row != row) {
1962 gtk_cmclist_unselect_all(clist);
1963 if (event->button == 2)
1964 folderview_select_node
1966 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1969 gtk_cmclist_select_row(clist, row, column);
1973 if (event->button != 3) return FALSE;
1975 folderview_set_sens_and_popup_menu(folderview, row, event);
1979 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1980 FolderView *folderview)
1982 int row = -1, column = -1;
1984 if (!event) return FALSE;
1986 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1989 if (event->button == 1 && folderview->open_folder == FALSE &&
1990 folderview->opened != NULL) {
1991 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1992 folderview->opened);
1993 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1999 #define BREAK_ON_MODIFIER_KEY() \
2000 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2002 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2003 FolderView *folderview)
2005 GtkCMCTreeNode *node;
2008 if (!event) return FALSE;
2010 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2013 switch (event->keyval) {
2015 if (folderview->selected) {
2016 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2017 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2018 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2019 folderview->selected);
2021 folderview_select_node(folderview,
2022 folderview->selected);
2026 case GDK_KEY_Return:
2027 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2028 gtk_cmctree_toggle_expansion(
2029 GTK_CMCTREE(folderview->ctree),
2030 folderview->selected);
2034 case GDK_KEY_Return:
2035 case GDK_KEY_KP_Enter:
2036 if (folderview->selected)
2037 folderview_select_node(folderview, folderview->selected);
2041 BREAK_ON_MODIFIER_KEY();
2042 if (folderview->selected) {
2043 if (folderview->opened == folderview->selected &&
2044 (!folderview->summaryview->folder_item ||
2045 folderview->summaryview->folder_item->total_msgs == 0))
2046 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2048 folderview_select_node(folderview,
2049 folderview->selected);
2053 if (folderview->selected) {
2054 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2055 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2056 folderview->selected);
2058 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2059 folderview->selected))) {
2060 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2061 NULL, folder_item_parent(item)))) {
2062 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2063 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2064 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2073 if (event->keyval == GDK_KEY_Home)
2074 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2076 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2077 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2079 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2081 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2082 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2092 typedef struct _PostponedSelectData
2095 GtkCMCTreeNode *row;
2097 FolderView *folderview;
2098 } PostponedSelectData;
2100 static gboolean postpone_select(void *data)
2102 PostponedSelectData *psdata = (PostponedSelectData *)data;
2103 debug_print("trying again\n");
2105 psdata->folderview->postpone_select_id = 0;
2106 psdata->folderview->open_folder = TRUE;
2107 main_window_cursor_normal(psdata->folderview->mainwin);
2108 STATUSBAR_POP(psdata->folderview->mainwin);
2109 folderview_selected(psdata->ctree, psdata->row,
2110 psdata->column, psdata->folderview);
2115 void folderview_close_opened(FolderView *folderview)
2117 if (folderview->opened) {
2118 FolderItem *olditem;
2120 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2121 folderview->opened);
2123 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2124 olditem->path ? olditem->path:olditem->name);
2125 /* will be null if we just moved the previously opened folder */
2126 STATUSBAR_PUSH(folderview->mainwin, buf);
2127 main_window_cursor_wait(folderview->mainwin);
2129 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2130 summary_show(folderview->summaryview, NULL);
2131 folder_item_close(olditem);
2132 main_window_cursor_normal(folderview->mainwin);
2133 STATUSBAR_POP(folderview->mainwin);
2134 if (olditem->folder->klass->item_closed)
2135 olditem->folder->klass->item_closed(olditem);
2140 if (folderview->opened &&
2141 !GTK_CMCTREE_ROW(folderview->opened)->children)
2142 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2144 folderview->opened = NULL;
2146 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2147 gint column, FolderView *folderview)
2149 static gboolean can_select = TRUE; /* exclusive lock */
2154 GtkCMCTreeNode *old_opened = folderview->opened;
2156 folderview->selected = row;
2158 debug_print("newly selected %p, opened %p\n", folderview->selected,
2159 folderview->opened);
2160 if (folderview->opened == row) {
2161 folderview->open_folder = FALSE;
2166 item = gtk_cmctree_node_get_row_data(ctree, row);
2169 folderview->open_folder = FALSE;
2173 if (!can_select || summary_is_locked(folderview->summaryview)) {
2174 if (folderview->opened) {
2175 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2176 gtk_cmctree_select(ctree, folderview->opened);
2178 folderview->open_folder = FALSE;
2183 if (!folderview->open_folder) {
2190 /* Save cache for old folder */
2191 /* We don't want to lose all caches if sylpheed crashed */
2192 /* resets folderview->opened to NULL */
2193 folderview_close_opened(folderview);
2195 /* CLAWS: set compose button type: news folder items
2196 * always have a news folder as parent */
2198 toolbar_set_compose_button
2199 (folderview->mainwin->toolbar,
2200 FOLDER_TYPE(item->folder) == F_NEWS ?
2201 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2204 debug_print("Folder %s is selected\n", item->path);
2206 if (!GTK_CMCTREE_ROW(row)->children)
2207 gtk_cmctree_expand(ctree, row);
2209 /* ungrab the mouse event */
2210 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2211 gtk_grab_remove(GTK_WIDGET(ctree));
2212 if (gdk_pointer_is_grabbed())
2213 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2217 /* TODO: wwp: avoid displaying (null) in the status bar */
2218 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2219 item->path : "(null)");
2220 debug_print("%s\n", buf);
2221 STATUSBAR_PUSH(folderview->mainwin, buf);
2224 main_window_cursor_wait(folderview->mainwin);
2226 if (folderview->scanning_folder == item->folder) {
2229 res = folder_item_open(item);
2232 if (res == -1 && item->no_select == FALSE) {
2233 main_window_cursor_normal(folderview->mainwin);
2234 STATUSBAR_POP(folderview->mainwin);
2236 alertpanel_error(_("Folder could not be opened."));
2238 folderview->open_folder = FALSE;
2242 } else if (res == -2 && item->no_select == FALSE) {
2243 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2244 data->ctree = ctree;
2246 data->column = column;
2247 data->folderview = folderview;
2248 debug_print("postponing open of %s till end of scan\n",
2249 item->path ? item->path:item->name);
2250 folderview->open_folder = FALSE;
2252 if (folderview->postpone_select_id != 0)
2253 g_source_remove(folderview->postpone_select_id);
2254 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2259 main_window_cursor_normal(folderview->mainwin);
2262 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2263 opened = summary_show(folderview->summaryview, item);
2265 folder_clean_cache_memory(item);
2268 gtkut_ctree_set_focus_row(ctree, old_opened);
2269 gtk_cmctree_select(ctree, old_opened);
2270 folderview->opened = old_opened;
2272 folderview->opened = row;
2273 if (gtk_cmctree_node_is_visible(ctree, row)
2274 != GTK_VISIBILITY_FULL)
2275 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2278 STATUSBAR_POP(folderview->mainwin);
2280 folderview->open_folder = FALSE;
2285 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2286 FolderView *folderview)
2290 item = gtk_cmctree_node_get_row_data(ctree, node);
2291 cm_return_if_fail(item != NULL);
2292 item->collapsed = FALSE;
2293 folderview_update_node(folderview, node);
2296 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2297 FolderView *folderview)
2301 item = gtk_cmctree_node_get_row_data(ctree, node);
2302 cm_return_if_fail(item != NULL);
2303 item->collapsed = TRUE;
2304 folderview_update_node(folderview, node);
2307 static void folderview_popup_close(GtkMenuShell *menu_shell,
2308 FolderView *folderview)
2310 if (!folderview->opened) return;
2312 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2315 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2316 FolderView *folderview)
2318 FolderColumnType type = folderview->col_state[column].type;
2320 prefs_common.folder_col_size[type] = width;
2323 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2325 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2326 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2327 GtkCMCTreeNode *node, *parent_node;
2328 gint *col_pos = folderview->col_pos;
2329 FolderItemUpdateData hookdata;
2331 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2332 if (parent_node == NULL)
2335 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2337 text[col_pos[F_COL_FOLDER]] = item->name;
2338 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2343 gtk_cmctree_expand(ctree, parent_node);
2344 gtk_cmctree_node_set_row_data(ctree, node, item);
2346 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2347 folderview_sort_folders(folderview, parent_node, item->folder);
2349 hookdata.item = item;
2350 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2351 hookdata.msg = NULL;
2352 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2354 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2357 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2359 FolderView *folderview = (FolderView *)data;
2361 GSList *mlist = NULL;
2363 FolderItem *special_trash = NULL;
2366 if (!folderview->selected) return;
2367 item = folderview_get_selected_item(folderview);
2368 cm_return_if_fail(item != NULL);
2369 cm_return_if_fail(item->folder != NULL);
2371 if (NULL != (ac = account_find_from_item(item)))
2372 special_trash = account_get_special_folder(ac, F_TRASH);
2374 if (item != item->folder->trash && item != special_trash
2375 && !folder_has_parent_of_type(item, F_TRASH)) return;
2377 if (prefs_common.ask_on_clean) {
2378 if (alertpanel(_("Empty trash"),
2379 _("Delete all messages in trash?"),
2380 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2384 mlist = folder_item_get_msg_list(item);
2386 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2387 MsgInfo * msginfo = (MsgInfo *) cur->data;
2388 if (MSG_IS_LOCKED(msginfo->flags))
2390 /* is it partially received? (partial_recv isn't cached) */
2391 if (msginfo->total_size != 0 &&
2392 msginfo->size != (off_t)msginfo->total_size)
2393 partial_mark_for_delete(msginfo);
2395 procmsg_msg_list_free(mlist);
2397 folder_item_remove_all_msg(item);
2400 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2402 FolderView *folderview = (FolderView *)data;
2404 FolderItem *special_queue = NULL;
2406 gchar *errstr = NULL;
2408 if (!folderview->selected) return;
2409 item = folderview_get_selected_item(folderview);
2410 cm_return_if_fail(item != NULL);
2411 cm_return_if_fail(item->folder != NULL);
2413 if (NULL != (ac = account_find_from_item(item)))
2414 special_queue = account_get_special_folder(ac, F_QUEUE);
2416 if (item != item->folder->queue && item != special_queue
2417 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2419 if (procmsg_queue_is_empty(item))
2422 if (prefs_common.work_offline)
2423 if (alertpanel(_("Offline warning"),
2424 _("You're working offline. Override?"),
2425 GTK_STOCK_NO, GTK_STOCK_YES,
2426 NULL) != G_ALERTALTERNATE)
2429 /* ask for confirmation before sending queued messages only
2430 in online mode and if there is at least one message queued
2431 in any of the folder queue
2433 if (prefs_common.confirm_send_queued_messages) {
2434 if (!prefs_common.work_offline) {
2435 if (alertpanel(_("Send queued messages"),
2436 _("Send all queued messages?"),
2437 GTK_STOCK_CANCEL, _("_Send"),
2438 NULL) != G_ALERTALTERNATE)
2443 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2445 alertpanel_error_log(_("Some errors occurred while "
2446 "sending queued messages."));
2448 alertpanel_error_log(_("Some errors occurred "
2449 "while sending queued messages:\n%s"), errstr);
2455 static void folderview_search_cb(GtkAction *action, gpointer data)
2457 FolderView *folderview = (FolderView *)data;
2458 summary_search(folderview->summaryview);
2461 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2463 FolderView *folderview = (FolderView *)data;
2466 if (!folderview->selected) return;
2468 item = folderview_get_selected_item(folderview);
2469 cm_return_if_fail(item != NULL);
2470 cm_return_if_fail(item->folder != NULL);
2472 item->processing_pending = TRUE;
2473 folder_item_apply_processing(item);
2474 item->processing_pending = FALSE;
2477 static void folderview_property_cb(GtkAction *action, gpointer data)
2479 FolderView *folderview = (FolderView *)data;
2482 if (!folderview->selected) return;
2484 item = folderview_get_selected_item(folderview);
2485 cm_return_if_fail(item != NULL);
2486 cm_return_if_fail(item->folder != NULL);
2488 prefs_folder_item_open(item);
2491 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2493 GSList *list = NULL;
2494 GSList *done = NULL;
2495 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2497 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2498 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2499 && list->data != node) {
2500 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2501 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2504 for (list = done; list != NULL; list = g_slist_next(list)) {
2505 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2511 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2512 FolderItem *to_folder, gboolean copy)
2514 FolderItem *new_folder = NULL;
2518 cm_return_if_fail(folderview != NULL);
2519 cm_return_if_fail(from_folder != NULL);
2520 cm_return_if_fail(to_folder != NULL);
2522 if (prefs_common.warn_dnd) {
2523 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2524 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2525 from_folder->name, to_folder->name);
2526 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2527 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2528 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2531 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2533 else if (status & G_ALERTDISABLE)
2534 prefs_common.warn_dnd = FALSE;
2537 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2538 from_folder->name, to_folder->name);
2539 STATUSBAR_PUSH(folderview->mainwin, buf);
2541 summary_clear_all(folderview->summaryview);
2542 folderview->opened = NULL;
2543 folderview->selected = NULL;
2544 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2546 main_window_cursor_wait(folderview->mainwin);
2548 statusbar_verbosity_set(FALSE);
2549 folder_item_update_freeze();
2550 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2551 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2552 statusbar_verbosity_set(FALSE);
2553 main_window_cursor_normal(folderview->mainwin);
2554 STATUSBAR_POP(folderview->mainwin);
2555 folder_item_update_thaw();
2556 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2558 folderview_sort_folders(folderview,
2559 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2560 NULL, to_folder), new_folder->folder);
2561 folderview_select(folderview, new_folder);
2562 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2564 statusbar_verbosity_set(FALSE);
2565 main_window_cursor_normal(folderview->mainwin);
2566 STATUSBAR_POP(folderview->mainwin);
2567 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2568 folder_item_update_thaw();
2570 case F_MOVE_FAILED_DEST_IS_PARENT:
2571 alertpanel_error(_("Source and destination are the same."));
2573 case F_MOVE_FAILED_DEST_IS_CHILD:
2574 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2575 _("Can't move a folder to one of its children."));
2577 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2578 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2581 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2586 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2589 static gint folderview_clist_compare(GtkCMCList *clist,
2590 gconstpointer ptr1, gconstpointer ptr2)
2592 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2593 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2595 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2597 return item1->order - item2->order;
2600 // if only one folder has an order it comes first
2601 if (item1->order > 0)
2605 if (item2->order > 0)
2611 return (item2->name != NULL);
2615 return g_utf8_collate(item1->name, item2->name);
2618 static void folderview_processing_cb(GtkAction *action, gpointer data)
2620 FolderView *folderview = (FolderView *)data;
2624 if (!folderview->selected) return;
2626 item = folderview_get_selected_item(folderview);
2627 cm_return_if_fail(item != NULL);
2628 cm_return_if_fail(item->folder != NULL);
2630 id = folder_item_get_identifier(item);
2631 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2634 prefs_filtering_open(&item->prefs->processing, title,
2635 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2639 void folderview_set_target_folder_color(gint color_op)
2643 FolderView *folderview;
2645 for (list = folderview_list; list != NULL; list = list->next) {
2646 folderview = (FolderView *)list->data;
2647 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2649 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2650 folderview->color_op;
2656 static gchar *last_smallfont = NULL;
2657 static gchar *last_normalfont = NULL;
2658 static gchar *last_boldfont = NULL;
2659 static gboolean last_derive = 0;
2661 void folderview_reinit_fonts(FolderView *folderview)
2664 g_free(last_smallfont);
2665 last_smallfont = NULL;
2666 g_free(last_normalfont);
2667 last_normalfont = NULL;
2668 g_free(last_boldfont);
2669 last_boldfont = NULL;
2672 void folderview_reflect_prefs(void)
2674 gboolean update_font = FALSE;
2675 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2676 FolderItem *item = folderview_get_selected_item(folderview);
2677 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2678 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2679 gint height = gtk_adjustment_get_value(pos);
2681 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2682 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2683 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2684 last_derive != prefs_common.derive_from_normal_font)
2687 g_free(last_smallfont);
2688 last_smallfont = g_strdup(SMALL_FONT);
2689 g_free(last_normalfont);
2690 last_normalfont = g_strdup(NORMAL_FONT);
2691 g_free(last_boldfont);
2692 last_boldfont = g_strdup(BOLD_FONT);
2693 last_derive = prefs_common.derive_from_normal_font;
2696 normal_style = normal_color_style = bold_style =
2697 bold_color_style = bold_tgtfold_style = NULL;
2699 folderview_init(folderview);
2701 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2702 folderview_column_set_titles(folderview);
2703 folderview_set_all();
2705 g_signal_handlers_block_by_func
2706 (G_OBJECT(folderview->ctree),
2707 G_CALLBACK(folderview_selected), folderview);
2710 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2711 GTK_CMCTREE(folderview->ctree), NULL, item);
2713 folderview_select(folderview, item);
2714 folderview->open_folder = FALSE;
2715 folderview->selected = node;
2718 g_signal_handlers_unblock_by_func
2719 (G_OBJECT(folderview->ctree),
2720 G_CALLBACK(folderview_selected), folderview);
2722 pos = gtk_scrolled_window_get_vadjustment(
2723 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2724 gtk_adjustment_set_value(pos, height);
2725 gtk_adjustment_changed(pos);
2726 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2729 static void drag_state_stop(FolderView *folderview)
2731 if (folderview->drag_timer_id)
2732 g_source_remove(folderview->drag_timer_id);
2733 folderview->drag_timer_id = 0;
2734 folderview->drag_node = NULL;
2737 static gboolean folderview_defer_expand(FolderView *folderview)
2739 if (folderview->drag_node) {
2740 folderview_recollapse_nodes(folderview, folderview->drag_node);
2741 if (folderview->drag_item->collapsed) {
2742 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2743 folderview->nodes_to_recollapse = g_slist_append
2744 (folderview->nodes_to_recollapse, folderview->drag_node);
2747 folderview->drag_item = NULL;
2748 folderview->drag_timer_id = 0;
2752 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2754 /* the idea is that we call drag_state_start() whenever we want expansion to
2755 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2756 * we need to call drag_state_stop() */
2757 drag_state_stop(folderview);
2758 /* request expansion */
2759 if (0 != (folderview->drag_timer_id = g_timeout_add
2760 (prefs_common.hover_timeout,
2761 (GSourceFunc)folderview_defer_expand,
2763 folderview->drag_node = node;
2764 folderview->drag_item = item;
2767 #ifndef GENERIC_UMPC
2768 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2769 FolderView *folderview)
2771 GdkDragContext *context;
2773 cm_return_if_fail(folderview != NULL);
2774 if (folderview->selected == NULL) return;
2775 if (folderview->nodes_to_recollapse)
2776 g_slist_free(folderview->nodes_to_recollapse);
2777 folderview->nodes_to_recollapse = NULL;
2778 context = gtk_drag_begin(widget, folderview->target_list,
2779 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2780 gtk_drag_set_icon_default(context);
2783 static void folderview_drag_data_get(GtkWidget *widget,
2784 GdkDragContext *drag_context,
2785 GtkSelectionData *selection_data,
2788 FolderView *folderview)
2792 gchar *source = NULL;
2793 if (info == TARGET_DUMMY) {
2794 sel = GTK_CMCLIST(folderview->ctree)->selection;
2798 item = gtk_cmctree_node_get_row_data
2799 (GTK_CMCTREE(folderview->ctree),
2800 GTK_CMCTREE_NODE(sel->data));
2802 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2803 gtk_selection_data_set(selection_data,
2804 gtk_selection_data_get_target(selection_data), 8,
2805 source, strlen(source));
2808 g_warning("unknown info %d", info);
2812 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2814 FolderUpdateData *hookdata;
2815 FolderView *folderview;
2819 folderview = (FolderView *) userdata;
2820 cm_return_val_if_fail(hookdata != NULL, FALSE);
2821 cm_return_val_if_fail(folderview != NULL, FALSE);
2823 ctree = folderview->ctree;
2824 cm_return_val_if_fail(ctree != NULL, FALSE);
2826 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2827 folderview_create_folder_node(folderview, hookdata->item);
2828 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2829 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2830 NULL, folder_item_parent(hookdata->item));
2831 folderview_sort_folders(folderview, node, hookdata->folder);
2832 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2833 GtkCMCTreeNode *node;
2835 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2837 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2838 if (folderview->selected == node)
2839 folderview->selected = NULL;
2840 if (folderview->opened == node)
2841 folderview->opened = NULL;
2843 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2844 /* do nothing, it's done by the ADD and REMOVE) */
2845 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2846 folderview_set(folderview);
2851 static gboolean folderview_dnd_scroll_cb(gpointer data)
2853 FolderView *folderview = (FolderView *)data;
2854 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2855 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2856 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2857 gint max = (int)gtk_adjustment_get_upper(pos) -
2858 (int)gtk_adjustment_get_page_size(pos);
2860 if (folderview->scroll_value == 0) {
2861 folderview->scroll_timeout_id = 0;
2865 if (folderview->scroll_value > 0 && new_val > max) {
2867 } else if (folderview->scroll_value < 0 && new_val < 0) {
2870 gtk_adjustment_set_value(pos, new_val);
2875 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2876 GdkDragContext *context,
2880 FolderView *folderview)
2883 FolderItem *item = NULL, *src_item = NULL;
2884 GtkCMCTreeNode *node = NULL;
2885 gboolean acceptable = FALSE;
2886 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2887 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2888 int height = (int)gtk_adjustment_get_page_size(pos);
2889 int total_height = (int)gtk_adjustment_get_upper(pos);
2890 int vpos = (int)gtk_adjustment_get_value(pos);
2891 int offset = prefs_common.show_col_headers ? 24:0;
2894 if (gtk_cmclist_get_selection_info
2895 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2896 GtkWidget *srcwidget;
2898 if (y > height - (48 - offset) && height + vpos < total_height) {
2899 dist = -(height - (48 - offset) - y);
2900 folderview->scroll_value = 1.41f * (1+(dist / 6));
2901 } else if (y < 72 - (24 - offset) && y >= 0) {
2902 dist = 72 - (24 - offset) - y;
2903 folderview->scroll_value = -1.41f * (1+(dist / 6));
2905 folderview->scroll_value = 0;
2907 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2908 folderview->scroll_timeout_id =
2909 g_timeout_add(30, folderview_dnd_scroll_cb,
2913 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2914 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2915 src_item = folderview->summaryview->folder_item;
2917 srcwidget = gtk_drag_get_source_widget(context);
2918 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2919 /* comes from summaryview */
2920 /* we are copying messages, so only accept folder items that are not
2921 the source item, are no root items and can copy messages */
2922 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2923 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2924 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2926 } else if (srcwidget == folderview->ctree) {
2927 /* comes from folderview */
2928 /* we are moving folder items, only accept folders that are not
2929 the source items and can copy messages and create folder items */
2930 if (item && item->folder && src_item && src_item != item &&
2931 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2932 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2933 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2934 || item->folder == src_item->folder))
2937 /* comes from another app */
2938 /* we are adding messages, so only accept folder items that are
2939 no root items and can copy messages */
2940 if (item && item->folder && folder_item_parent(item) != NULL
2941 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2942 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2947 if (acceptable || (src_item && src_item == item))
2948 drag_state_start(folderview, node, item);
2951 g_signal_handlers_block_by_func
2953 G_CALLBACK(folderview_selected), folderview);
2954 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2955 g_signal_handlers_unblock_by_func
2957 G_CALLBACK(folderview_selected), folderview);
2958 gdk_drag_status(context,
2959 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2960 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2962 if (folderview->opened)
2963 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2964 gdk_drag_status(context, 0, time);
2970 static void folderview_drag_leave_cb(GtkWidget *widget,
2971 GdkDragContext *context,
2973 FolderView *folderview)
2975 drag_state_stop(folderview);
2976 folderview->scroll_value = 0;
2977 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2980 static void free_info (gpointer stuff, gpointer data)
2985 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2986 guint time, FolderItem *item)
2989 GSList *msglist = NULL;
2990 list = uri_list_extract_filenames(data);
2991 if (!(item && item->folder && folder_item_parent(item) != NULL
2992 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2994 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2995 debug_print("item doesn't fit\n");
2999 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3000 debug_print("list is empty\n");
3003 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3004 MsgFileInfo *info = NULL;
3006 if (file_is_email((gchar *)tmp->data)) {
3007 info = g_new0(MsgFileInfo, 1);
3008 info->msginfo = NULL;
3009 info->file = (gchar *)tmp->data;
3010 msglist = g_slist_prepend(msglist, info);
3011 debug_print("file is a mail\n");
3013 debug_print("file isn't a mail\n");
3017 msglist = g_slist_reverse(msglist);
3018 folder_item_add_msgs(item, msglist, FALSE);
3019 g_slist_foreach(msglist, free_info, NULL);
3020 g_slist_free(msglist);
3021 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3023 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3025 list_free_strings(list);
3029 static void folderview_drag_received_cb(GtkWidget *widget,
3030 GdkDragContext *drag_context,
3033 GtkSelectionData *data,
3036 FolderView *folderview)
3039 FolderItem *item = NULL, *src_item;
3040 GtkCMCTreeNode *node;
3041 int offset = prefs_common.show_col_headers ? 24:0;
3043 folderview->scroll_value = 0;
3045 if (info == TARGET_DUMMY) {
3046 drag_state_stop(folderview);
3047 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3048 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3049 /* comes from summaryview */
3050 if (gtk_cmclist_get_selection_info
3051 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3054 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3055 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3056 src_item = folderview->summaryview->folder_item;
3058 if (item->no_select) {
3059 alertpanel_error(_("The destination folder can only be used to "
3060 "store subfolders."));
3063 /* re-check (due to acceptable possibly set for folder moves */
3064 if (!(item && item->folder && item->path && !item->no_select &&
3065 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3068 if (item && src_item) {
3069 switch (gdk_drag_context_get_selected_action(drag_context)) {
3070 case GDK_ACTION_COPY:
3071 summary_copy_selected_to(folderview->summaryview, item);
3072 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3074 case GDK_ACTION_MOVE:
3075 case GDK_ACTION_DEFAULT:
3077 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3078 summary_copy_selected_to(folderview->summaryview, item);
3080 summary_move_selected_to(folderview->summaryview, item);
3081 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3084 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3086 /* comes from folderview */
3088 gboolean folder_is_normal = TRUE;
3089 gboolean copy = (GDK_ACTION_COPY ==
3090 gdk_drag_context_get_selected_action(drag_context));
3092 source = (char *)gtk_selection_data_get_data(data) + 17;
3093 if (gtk_cmclist_get_selection_info
3094 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3096 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3099 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3100 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3101 src_item = folder_find_item_from_identifier(source);
3105 src_item->stype == F_NORMAL &&
3106 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3107 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3108 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3109 !folder_has_parent_of_type(src_item, F_TRASH);
3110 if (!item || !src_item || !folder_is_normal) {
3111 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3115 folderview_move_folder(folderview, src_item, item, copy);
3116 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3118 folderview->nodes_to_recollapse = NULL;
3119 } else if (info == TARGET_MAIL_URI_LIST) {
3120 if (gtk_cmclist_get_selection_info
3121 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3124 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3126 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3127 debug_print("no node\n");
3130 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3132 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3133 debug_print("no item\n");
3136 folderview_finish_dnd(gtk_selection_data_get_data(data),
3137 drag_context, time, item);
3141 static void folderview_drag_end_cb(GtkWidget *widget,
3142 GdkDragContext *drag_context,
3143 FolderView *folderview)
3145 drag_state_stop(folderview);
3146 folderview->scroll_value = 0;
3147 g_slist_free(folderview->nodes_to_recollapse);
3148 folderview->nodes_to_recollapse = NULL;
3151 void folderview_register_popup(FolderViewPopup *fpopup)
3155 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3156 FolderView *folderview = folderviews->data;
3157 GtkActionGroup *factory;
3159 factory = create_action_group(folderview, fpopup);
3160 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3162 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3165 void folderview_unregister_popup(FolderViewPopup *fpopup)
3170 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3171 FolderView *folderview = folderviews->data;
3173 g_hash_table_remove(folderview->popups, fpopup->klass);
3175 g_hash_table_remove(folderview_popups, fpopup->klass);