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 = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
556 if (folderview->opened)
557 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
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 = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
773 if (folderview->opened)
774 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
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;
834 item = folderview_get_selected_item(folderview);
838 if (folderview->summaryview->folder_item != item
839 && prefs_common.ask_mark_all_read) {
840 val = alertpanel_full(_("Mark all as read"),
841 _("Do you really want to mark all mails in this "
842 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
843 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
845 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
847 else if (val & G_ALERTDISABLE)
848 prefs_common.ask_mark_all_read = FALSE;
852 folder_item_update_freeze();
853 if (folderview->summaryview->folder_item != item && !recursive)
854 summary_lock(folderview->summaryview);
856 summary_freeze(folderview->summaryview);
859 folderutils_mark_all_read_recursive(item);
861 folderutils_mark_all_read(item);
863 if (folderview->summaryview->folder_item != item && !recursive)
864 summary_unlock(folderview->summaryview);
866 summary_thaw(folderview->summaryview);
867 folder_item_update_thaw();
870 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
872 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
874 cm_return_if_fail(node != NULL);
876 if (folderview->open_folder) {
880 gtkut_ctree_expand_parent_all(ctree, node);
882 folderview->open_folder = TRUE;
883 gtkut_ctree_set_focus_row(ctree, node);
884 gtk_cmctree_select(ctree, node);
885 if ((folderview->summaryview->folder_item &&
886 folderview->summaryview->folder_item->total_msgs > 0) ||
887 prefs_common.layout_mode == SMALL_LAYOUT)
888 summary_grab_focus(folderview->summaryview);
890 gtk_widget_grab_focus(folderview->ctree);
893 void folderview_unselect(FolderView *folderview)
895 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
897 (GTK_CMCTREE(folderview->ctree), folderview->opened);
899 folderview->selected = folderview->opened = NULL;
902 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
903 GtkCMCTreeNode *node,
909 node = gtkut_ctree_node_next(ctree, node);
911 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
913 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
914 item = gtk_cmctree_node_get_row_data(ctree, node);
917 if (item->stype == F_TRASH || item->stype == F_DRAFT)
921 if(item->unread_msgs > 0)
925 if(item->new_msgs > 0)
929 if(item->marked_msgs > 0)
938 void folderview_select_next_with_flag(FolderView *folderview,
942 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
943 GtkCMCTreeNode *node = NULL;
944 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
945 gboolean last_open = prefs_common.always_show_msg;
949 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
952 prefs_common.summary_select_prio[0] = ACTION_NEW;
955 prefs_common.summary_select_prio[0] = ACTION_MARKED;
958 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
960 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
962 folderview_select_node(folderview, node);
966 if (!folderview->opened ||
967 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
971 /* search again from the first node */
972 node = folderview_find_next_with_flag(ctree, NULL, flag);
974 folderview_select_node(folderview, node);
977 prefs_common.summary_select_prio[0] = last_summary_select_prio;
978 prefs_common.always_show_msg = last_open;
981 FolderItem *folderview_get_selected_item(FolderView *folderview)
983 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
985 if (!folderview->selected) return NULL;
986 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
989 static void folderview_set_folders(FolderView *folderview)
992 list = folder_get_list();
994 for (; list != NULL; list = list->next) {
995 folderview_append_folder(folderview, FOLDER(list->data));
999 static gchar *get_scan_str(FolderItem *item)
1002 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1003 item->folder->name, G_DIR_SEPARATOR,
1006 return g_strdup_printf(_("Scanning folder %s..."),
1007 item->folder->name);
1009 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1013 for (list = folderview_list; list != NULL; list = list->next) {
1014 FolderView *folderview = (FolderView *)list->data;
1015 MainWindow *mainwin = folderview->mainwin;
1016 gchar *str = get_scan_str(item);
1018 STATUSBAR_PUSH(mainwin, str);
1019 STATUSBAR_POP(mainwin);
1024 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1027 MainWindow *mainwin = mainwindow_get_mainwindow();
1028 FolderView *folderview = NULL;
1029 GtkAdjustment *pos = NULL;
1032 cm_return_if_fail(folder != NULL);
1034 if (!folder->klass->scan_tree) return;
1037 alertpanel_full(_("Rebuild folder tree"),
1038 _("Rebuilding the folder tree will remove "
1039 "local caches. Do you want to continue?"),
1040 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1041 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1042 != G_ALERTALTERNATE) {
1048 window = label_window_create(_("Rebuilding folder tree..."));
1050 window = label_window_create(_("Scanning folder tree..."));
1053 folderview = mainwin->folderview;
1056 pos = gtk_scrolled_window_get_vadjustment(
1057 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1058 height = gtk_adjustment_get_value(pos);
1061 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1062 folder_scan_tree(folder, rebuild);
1063 folder_set_ui_func(folder, NULL, NULL);
1065 folderview_set_all();
1068 pos = gtk_scrolled_window_get_vadjustment(
1069 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1070 gtk_adjustment_set_value(pos, height);
1071 gtk_adjustment_changed(pos);
1073 label_window_destroy(window);
1077 /** folderview_check_new()
1078 * Scan and update the folder and return the
1079 * count the number of new messages since last check.
1080 * \param folder the folder to check for new messages
1081 * \return the number of new messages since last check
1083 gint folderview_check_new(Folder *folder)
1087 FolderView *folderview;
1089 GtkCMCTreeNode *node;
1091 gint former_new_msgs = 0;
1092 gint former_new = 0, former_unread = 0, former_total;
1094 for (list = folderview_list; list != NULL; list = list->next) {
1095 folderview = (FolderView *)list->data;
1096 ctree = GTK_CMCTREE(folderview->ctree);
1097 folderview->scanning_folder = folder;
1099 main_window_lock(folderview->mainwin);
1101 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1102 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1104 item = gtk_cmctree_node_get_row_data(ctree, node);
1105 if (!item || !item->path || !item->folder) continue;
1106 if (item->no_select) continue;
1107 if (folder && folder != item->folder) continue;
1108 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1109 if (!item->prefs->newmailcheck) continue;
1110 if (item->processing_pending == TRUE) {
1111 debug_print("skipping %s, processing pending\n",
1112 item->path ? item->path : item->name);
1115 if (item->scanning != ITEM_NOT_SCANNING) {
1116 debug_print("skipping %s, scanning\n",
1117 item->path ? item->path : item->name);
1121 str = get_scan_str(item);
1123 STATUSBAR_PUSH(folderview->mainwin, str);
1127 folderview_scan_tree_func(item->folder, item, NULL);
1128 former_new = item->new_msgs;
1129 former_unread = item->unread_msgs;
1130 former_total = item->total_msgs;
1132 if (item->folder->klass->scan_required &&
1133 (item->folder->klass->scan_required(item->folder, item) ||
1134 item->folder->inbox == item ||
1135 item->opened == TRUE ||
1136 item->processing_pending == TRUE)) {
1137 if (folder_item_scan(item) < 0) {
1139 summaryview_unlock(folderview->summaryview, item);
1140 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1141 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1142 item->path ? item->path:item->name);
1143 STATUSBAR_POP(folderview->mainwin);
1145 } else if (!FOLDER_IS_LOCAL(folder)) {
1146 STATUSBAR_POP(folderview->mainwin);
1151 } else if (!item->folder->klass->scan_required) {
1152 if (folder_item_scan(item) < 0) {
1153 summaryview_unlock(folderview->summaryview, item);
1154 if (folder && !FOLDER_IS_LOCAL(folder)) {
1155 STATUSBAR_POP(folderview->mainwin);
1160 if (former_new != item->new_msgs ||
1161 former_unread != item->unread_msgs ||
1162 former_total != item->total_msgs)
1163 folderview_update_node(folderview, node);
1165 new_msgs += item->new_msgs;
1166 former_new_msgs += former_new;
1167 STATUSBAR_POP(folderview->mainwin);
1169 folderview->scanning_folder = NULL;
1170 main_window_unlock(folderview->mainwin);
1174 folder_write_list();
1175 /* Number of new messages since last check is the just the difference
1176 * between former_new_msgs and new_msgs. If new_msgs is less than
1177 * former_new_msgs, that would mean another session accessed the folder
1178 * and the result is not well defined.
1180 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1184 void folderview_check_new_all(void)
1188 FolderView *folderview;
1190 folderview = (FolderView *)folderview_list->data;
1193 main_window_lock(folderview->mainwin);
1194 window = label_window_create
1195 (_("Checking for new messages in all folders..."));
1197 list = folder_get_list();
1198 for (; list != NULL; list = list->next) {
1199 Folder *folder = list->data;
1201 folderview_check_new(folder);
1204 folder_write_list();
1205 folderview_set_all();
1207 label_window_destroy(window);
1208 main_window_unlock(folderview->mainwin);
1212 static gboolean folderview_have_children_sub(FolderView *folderview,
1218 if (!item || !item->folder || !item->folder->node)
1221 node = item->folder->node;
1223 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1224 node = node->children;
1226 if (in_sub && item->total_msgs > 0) {
1230 while (node != NULL) {
1231 if (node && node->data) {
1232 FolderItem *next_item = (FolderItem*) node->data;
1234 if (folderview_have_children_sub(folderview,
1243 static gboolean folderview_have_children(FolderView *folderview,
1246 return folderview_have_children_sub(folderview, item, FALSE);
1249 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1255 if (!item || !item->folder || !item->folder->node)
1258 node = item->folder->node;
1260 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1261 node = node->children;
1264 (item->new_msgs > 0 ||
1265 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1269 while (node != NULL) {
1270 if (node && node->data) {
1271 FolderItem *next_item = (FolderItem*) node->data;
1273 if (folderview_have_new_children_sub(folderview,
1282 static gboolean folderview_have_new_children(FolderView *folderview,
1285 return folderview_have_new_children_sub(folderview, item, FALSE);
1288 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1294 if (!item || !item->folder || !item->folder->node)
1297 node = item->folder->node;
1299 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1300 node = node->children;
1303 (item->unread_msgs > 0 ||
1304 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1308 while (node != NULL) {
1309 if (node && node->data) {
1310 FolderItem *next_item = (FolderItem*) node->data;
1312 if (folderview_have_unread_children_sub(folderview,
1322 static gboolean folderview_have_unread_children(FolderView *folderview,
1325 return folderview_have_unread_children_sub(folderview, item, FALSE);
1328 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1334 if (!item || !item->folder || !item->folder->node)
1337 node = item->folder->node;
1339 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1340 node = node->children;
1342 if (in_sub && item->search_match){
1346 while (node != NULL) {
1347 if (node && node->data) {
1348 FolderItem *next_item = (FolderItem*) node->data;
1350 if (folderview_have_matching_children_sub(folderview,
1360 static gboolean folderview_have_matching_children(FolderView *folderview,
1363 return folderview_have_matching_children_sub(folderview, item, FALSE);
1366 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1372 if (!item || !item->folder || !item->folder->node)
1375 node = item->folder->node;
1377 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1378 node = node->children;
1380 if (item->marked_msgs != 0) {
1384 while (node != NULL) {
1385 if (node && node->data) {
1386 FolderItem *next_item = (FolderItem*) node->data;
1388 if (folderview_have_marked_children_sub(folderview,
1397 static gboolean folderview_have_marked_children(FolderView *folderview,
1400 return folderview_have_marked_children_sub(folderview, item, FALSE);
1403 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1405 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1406 GtkStyle *style = NULL;
1407 GtkStyle *color_style = NULL;
1409 GdkPixbuf *xpm, *openxpm;
1410 static GdkPixbuf *searchicon;
1411 gboolean mark = FALSE;
1414 gboolean add_unread_mark;
1415 gboolean add_sub_match_mark;
1416 gboolean use_bold, use_color;
1417 gint *col_pos = folderview->col_pos;
1418 SpecialFolderItemType stype;
1420 item = gtk_cmctree_node_get_row_data(ctree, node);
1421 cm_return_if_fail(item != NULL);
1423 if (!GTK_CMCTREE_ROW(node)->expanded)
1424 mark = folderview_have_marked_children(folderview, item);
1426 mark = (item->marked_msgs != 0);
1428 stype = item->stype;
1429 if (stype == F_NORMAL) {
1430 if (folder_has_parent_of_type(item, F_TRASH))
1432 else if (folder_has_parent_of_type(item, F_DRAFT))
1434 else if (folder_has_parent_of_type(item, F_OUTBOX))
1436 else if (folder_has_parent_of_type(item, F_QUEUE))
1441 if (item->hide_read_msgs || item->hide_read_threads) {
1442 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1443 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1445 xpm = mark?m_inboxxpm:inboxxpm;
1446 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1450 if (item->hide_read_msgs || item->hide_read_threads) {
1451 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1452 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1454 xpm = mark?m_outboxxpm:outboxxpm;
1455 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1459 if (item->hide_read_msgs || item->hide_read_threads) {
1460 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1461 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1463 xpm = mark?m_queuexpm:queuexpm;
1464 openxpm = mark?m_queueopenxpm:queueopenxpm;
1468 if (item->hide_read_msgs || item->hide_read_threads) {
1469 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1470 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1472 xpm = mark?m_trashxpm:trashxpm;
1473 openxpm = mark?m_trashopenxpm:trashopenxpm;
1477 xpm = mark?m_draftsxpm:draftsxpm;
1478 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1481 if (item->hide_read_msgs || item->hide_read_threads) {
1482 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1483 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1485 xpm = mark?m_folderxpm:folderxpm;
1486 openxpm = mark?m_folderopenxpm:folderopenxpm;
1490 if (item->no_select) {
1491 xpm = openxpm = noselectxpm;
1494 name = folder_item_get_name(item);
1496 if (!GTK_CMCTREE_ROW(node)->expanded) {
1497 add_unread_mark = folderview_have_unread_children(
1499 add_sub_match_mark = folderview_have_matching_children(
1502 add_unread_mark = FALSE;
1503 add_sub_match_mark = FALSE;
1506 if (item->search_match) {
1508 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1511 xpm = openxpm = searchicon;
1515 if (prefs_common.display_folder_unread) {
1516 if (folder_has_parent_of_type(item, F_QUEUE)) {
1517 /* only total_msgs matters here */
1518 if (item->total_msgs > 0) {
1519 /* show total number (should be equal to the unread number)
1521 str = g_strdup_printf("%s (%d%s%s)",
1522 name, item->total_msgs,
1523 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1524 (item->unreadmarked_msgs > 0) ? "!" : "");
1527 if (prefs_common.display_folder_unread == 1) {
1528 if (item->unread_msgs > 0) {
1529 /* show unread number and signs */
1530 str = g_strdup_printf("%s (%d%s%s)",
1531 name, item->unread_msgs,
1532 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1533 (item->unreadmarked_msgs > 0) ? "!" : "");
1536 if (item->total_msgs > 0) {
1537 /* show unread number, total number and signs if any */
1538 str = g_strdup_printf("%s (%d/%d%s%s)",
1539 name, item->unread_msgs, item->total_msgs,
1540 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1541 (item->unreadmarked_msgs > 0) ? "!" : "");
1545 if ((str == NULL) &&
1546 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1547 /* no unread/total numbers, but at least one sign */
1548 str = g_strdup_printf("%s (%s%s)",
1550 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1551 (item->unreadmarked_msgs > 0) ? "!" : "");
1555 /* last fallback, folder name only or with +! sign */
1556 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1557 str = g_strdup_printf("%s%s",
1559 } else if (item->unreadmarked_msgs > 0) {
1560 str = g_strdup_printf("%s%s",
1562 } else if (add_sub_match_mark) {
1563 str = g_strdup_printf("%s%s",
1566 str = g_strdup_printf("%s", name);
1569 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1571 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1575 if (!folder_item_parent(item)) {
1576 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1577 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1578 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1580 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1581 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1582 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1585 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1586 folder_has_parent_of_type(item, F_TRASH)) {
1587 use_bold = use_color = FALSE;
1588 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1589 GSList *list = folder_item_get_msg_list(item);
1591 use_bold = use_color = FALSE;
1592 for (cur = list; cur; cur = cur->next) {
1593 MsgInfo *msginfo = (MsgInfo *)cur->data;
1594 if (!MSG_IS_DELETED(msginfo->flags)) {
1595 /* highlight queue folder if there are any messages */
1596 use_bold = use_color = TRUE;
1600 if (!GTK_CMCTREE_ROW(node)->expanded &&
1601 use_bold == FALSE &&
1602 folderview_have_children(folderview, item))
1603 use_bold = use_color = TRUE;
1604 procmsg_msg_list_free(list);
1606 /* if unread messages exist, print with bold font */
1607 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1609 /* if new messages exist, print with colored letter */
1611 (item->new_msgs > 0) ||
1613 folderview_have_new_children(folderview, item));
1616 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1621 if (item->prefs->color > 0 && !use_color) {
1622 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1623 color_style = gtk_style_copy(bold_style);
1624 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1625 style = color_style;
1626 } else if (use_color) {
1627 style = bold_color_style;
1630 if (item->op_count > 0) {
1631 style = bold_tgtfold_style;
1633 } else if (use_color) {
1634 style = normal_color_style;
1635 gtk_cmctree_node_set_foreground(ctree, node,
1636 &folderview->color_new);
1637 } else if (item->op_count > 0) {
1638 style = bold_tgtfold_style;
1639 } else if (item->prefs->color > 0) {
1641 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1642 color_style = gtk_style_copy(normal_style);
1643 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1644 style = color_style;
1646 style = normal_style;
1649 gtk_cmctree_node_set_row_style(ctree, node, style);
1651 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1652 folderview_update_node(folderview, node);
1655 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1658 FolderView *folderview;
1660 GtkCMCTreeNode *node;
1662 cm_return_if_fail(item != NULL);
1664 for (list = folderview_list; list != NULL; list = list->next) {
1665 folderview = (FolderView *)list->data;
1666 ctree = GTK_CMCTREE(folderview->ctree);
1668 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1669 if (node && item->search_match != matches) {
1670 item->search_match = matches;
1671 folderview_update_node(folderview, node);
1676 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1678 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1679 FolderView *folderview = (FolderView *)data;
1681 GtkCMCTreeNode *node;
1682 cm_return_val_if_fail(update_info != NULL, TRUE);
1683 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1684 cm_return_val_if_fail(folderview != NULL, FALSE);
1686 ctree = GTK_CMCTREE(folderview->ctree);
1688 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1691 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1692 folderview_update_node(folderview, node);
1694 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1695 update_info->item == folderview->summaryview->folder_item &&
1696 update_info->item != NULL)
1697 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1698 summary_show(folderview->summaryview, update_info->item);
1704 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1705 GNode *gnode, GtkCMCTreeNode *cnode,
1708 FolderView *folderview = (FolderView *)data;
1709 FolderItem *item = FOLDER_ITEM(gnode->data);
1711 cm_return_val_if_fail(item != NULL, FALSE);
1713 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1714 folderview_update_node(folderview, cnode);
1719 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1722 FolderView *folderview = (FolderView *)data;
1725 if (GTK_CMCTREE_ROW(node)->children) {
1726 item = gtk_cmctree_node_get_row_data(ctree, node);
1727 cm_return_if_fail(item != NULL);
1729 if (!item->collapsed)
1730 gtk_cmctree_expand(ctree, node);
1732 folderview_update_node(folderview, node);
1736 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1737 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1740 GtkCMCTreeNode *node, *parent, *sibling;
1742 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1744 g_warning("%s not found.\n", item->path);
1746 parent = GTK_CMCTREE_ROW(node)->parent;
1747 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1748 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1750 sibling = GTK_CMCTREE_ROW(parent)->children;
1754 tmp = gtk_cmctree_node_get_row_data
1756 if (tmp && tmp->stype != F_NORMAL)
1757 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1761 if (node != sibling)
1762 gtk_cmctree_move(ctree, node, parent, sibling);
1769 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1772 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1773 GtkCMCTreeNode *prev = NULL;
1775 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1776 gtk_sctree_sort_recursive(ctree, root);
1777 if (root && GTK_CMCTREE_ROW(root)->parent) {
1778 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1781 set_special_folder(ctree, folder->inbox, root, &prev);
1782 set_special_folder(ctree, folder->outbox, root, &prev);
1783 set_special_folder(ctree, folder->draft, root, &prev);
1784 set_special_folder(ctree, folder->queue, root, &prev);
1785 set_special_folder(ctree, folder->trash, root, &prev);
1786 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1789 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1791 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1792 GtkCMCTreeNode *root;
1794 cm_return_if_fail(folder != NULL);
1796 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1797 folderview_gnode_func, folderview);
1798 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1800 folderview_sort_folders(folderview, root, folder);
1803 /* callback functions */
1804 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1805 GdkEventButton *event)
1809 FolderViewPopup *fpopup;
1810 GtkActionGroup *action_group;
1812 FolderItem *special_trash = NULL, *special_queue = NULL;
1814 GtkUIManager *ui_manager = gtk_ui_manager_new();
1816 if (folderview->ui_manager)
1817 g_object_unref(folderview->ui_manager);
1819 folderview->ui_manager = ui_manager;
1820 item = folderview_get_selected_item(folderview);
1822 cm_return_if_fail(item != NULL);
1823 cm_return_if_fail(item->folder != NULL);
1824 folder = item->folder;
1826 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1829 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1831 fpopup = g_hash_table_lookup(folderview_popups, "common");
1832 action_group = g_hash_table_lookup(folderview->popups, "common");
1835 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1836 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1837 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1839 if (fpopup->add_menuitems)
1840 fpopup->add_menuitems(ui_manager, item);
1842 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1843 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1844 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1845 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1846 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1847 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1848 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1850 if (fpopup->set_sensitivity != NULL)
1851 fpopup->set_sensitivity(ui_manager, item);
1853 if (NULL != (ac = account_find_from_item(item))) {
1854 special_trash = account_get_special_folder(ac, F_TRASH);
1855 special_queue = account_get_special_folder(ac, F_QUEUE);
1858 if ((item == folder->trash || item == special_trash
1859 || folder_has_parent_of_type(item, F_TRASH))) {
1860 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1864 if ((item == folder->queue || item == special_queue
1865 || folder_has_parent_of_type(item, F_QUEUE))) {
1866 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1867 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1870 #define SET_SENS(name, sens) \
1871 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1873 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1874 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1875 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1876 folderview->selected == folderview->opened);
1877 SET_SENS("FolderViewPopup/Properties", TRUE);
1879 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1880 item->total_msgs >= 1 && !item->processing_pending);
1881 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1882 !item->no_select && !item->processing_pending);
1884 if (item == folder->trash || item == special_trash
1885 || folder_has_parent_of_type(item, F_TRASH)) {
1886 GSList *msglist = folder_item_get_msg_list(item);
1887 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1888 procmsg_msg_list_free(msglist);
1890 if (item == folder->queue || item == special_queue
1891 || folder_has_parent_of_type(item, F_QUEUE)) {
1892 GSList *msglist = folder_item_get_msg_list(item);
1893 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1894 procmsg_msg_list_free(msglist);
1898 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1899 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1900 g_signal_connect(G_OBJECT(popup), "selection_done",
1901 G_CALLBACK(folderview_popup_close),
1903 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1904 event->button, event->time);
1907 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1908 FolderView *folderview)
1910 GtkCMCList *clist = GTK_CMCLIST(ctree);
1911 gint prev_row = -1, row = -1, column = -1;
1913 if (!event) return FALSE;
1915 if (event->button == 1 || event->button == 2) {
1916 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1917 folderview->open_folder = TRUE;
1919 if (event->type == GDK_2BUTTON_PRESS) {
1920 if (clist->selection) {
1921 GtkCMCTreeNode *node;
1923 node = GTK_CMCTREE_NODE(clist->selection->data);
1925 gtk_cmctree_toggle_expansion(
1928 folderview->open_folder = FALSE;
1935 if (event->button == 2 || event->button == 3) {
1937 if (clist->selection) {
1938 GtkCMCTreeNode *node;
1940 node = GTK_CMCTREE_NODE(clist->selection->data);
1942 prev_row = gtkut_ctree_get_nth_from_node
1943 (GTK_CMCTREE(ctree), node);
1946 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1949 if (prev_row != row) {
1950 gtk_cmclist_unselect_all(clist);
1951 if (event->button == 2)
1952 folderview_select_node
1954 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1957 gtk_cmclist_select_row(clist, row, column);
1961 if (event->button != 3) return FALSE;
1963 folderview_set_sens_and_popup_menu(folderview, row, event);
1967 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1968 FolderView *folderview)
1970 int row = -1, column = -1;
1972 if (!event) return FALSE;
1974 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1977 if (event->button == 1 && folderview->open_folder == FALSE &&
1978 folderview->opened != NULL) {
1979 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1980 folderview->opened);
1981 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1987 #define BREAK_ON_MODIFIER_KEY() \
1988 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1990 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1991 FolderView *folderview)
1993 if (!event) return FALSE;
1995 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
1998 switch (event->keyval) {
2000 #ifndef GENERIC_UMPC
2001 case GDK_KEY_Return:
2002 case GDK_KEY_KP_Enter:
2004 if (folderview->selected) {
2005 folderview_select_node(folderview,
2006 folderview->selected);
2010 case GDK_KEY_Return:
2011 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2012 gtk_cmctree_toggle_expansion(
2013 GTK_CMCTREE(folderview->ctree),
2014 folderview->selected);
2019 BREAK_ON_MODIFIER_KEY();
2020 if (folderview->selected) {
2021 if (folderview->opened == folderview->selected &&
2022 (!folderview->summaryview->folder_item ||
2023 folderview->summaryview->folder_item->total_msgs == 0))
2024 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2026 folderview_select_node(folderview,
2027 folderview->selected);
2037 typedef struct _PostponedSelectData
2040 GtkCMCTreeNode *row;
2042 FolderView *folderview;
2043 } PostponedSelectData;
2045 static gboolean postpone_select(void *data)
2047 PostponedSelectData *psdata = (PostponedSelectData *)data;
2048 debug_print("trying again\n");
2050 psdata->folderview->postpone_select_id = 0;
2051 psdata->folderview->open_folder = TRUE;
2052 main_window_cursor_normal(psdata->folderview->mainwin);
2053 STATUSBAR_POP(psdata->folderview->mainwin);
2054 folderview_selected(psdata->ctree, psdata->row,
2055 psdata->column, psdata->folderview);
2060 void folderview_close_opened(FolderView *folderview)
2062 if (folderview->opened) {
2063 FolderItem *olditem;
2065 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2066 folderview->opened);
2068 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2069 olditem->path ? olditem->path:olditem->name);
2070 /* will be null if we just moved the previously opened folder */
2071 STATUSBAR_PUSH(folderview->mainwin, buf);
2072 main_window_cursor_wait(folderview->mainwin);
2074 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2075 summary_show(folderview->summaryview, NULL);
2076 folder_item_close(olditem);
2077 main_window_cursor_normal(folderview->mainwin);
2078 STATUSBAR_POP(folderview->mainwin);
2079 if (olditem->folder->klass->item_closed)
2080 olditem->folder->klass->item_closed(olditem);
2085 if (folderview->opened &&
2086 !GTK_CMCTREE_ROW(folderview->opened)->children)
2087 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2089 folderview->opened = NULL;
2091 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2092 gint column, FolderView *folderview)
2094 static gboolean can_select = TRUE; /* exclusive lock */
2099 GtkCMCTreeNode *old_opened = folderview->opened;
2101 folderview->selected = row;
2103 debug_print("newly selected %p, opened %p\n", folderview->selected,
2104 folderview->opened);
2105 if (folderview->opened == row) {
2106 folderview->open_folder = FALSE;
2111 item = gtk_cmctree_node_get_row_data(ctree, row);
2114 folderview->open_folder = FALSE;
2118 if (!can_select || summary_is_locked(folderview->summaryview)) {
2119 if (folderview->opened) {
2120 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2121 gtk_cmctree_select(ctree, folderview->opened);
2123 folderview->open_folder = FALSE;
2128 if (!folderview->open_folder) {
2135 /* Save cache for old folder */
2136 /* We don't want to lose all caches if sylpheed crashed */
2137 /* resets folderview->opened to NULL */
2138 folderview_close_opened(folderview);
2140 /* CLAWS: set compose button type: news folder items
2141 * always have a news folder as parent */
2143 toolbar_set_compose_button
2144 (folderview->mainwin->toolbar,
2145 FOLDER_TYPE(item->folder) == F_NEWS ?
2146 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2149 debug_print("Folder %s is selected\n", item->path);
2151 if (!GTK_CMCTREE_ROW(row)->children)
2152 gtk_cmctree_expand(ctree, row);
2154 /* ungrab the mouse event */
2155 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2156 gtk_grab_remove(GTK_WIDGET(ctree));
2157 if (gdk_pointer_is_grabbed())
2158 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2162 /* TODO: wwp: avoid displaying (null) in the status bar */
2163 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2164 item->path : "(null)");
2165 debug_print("%s\n", buf);
2166 STATUSBAR_PUSH(folderview->mainwin, buf);
2169 main_window_cursor_wait(folderview->mainwin);
2171 if (folderview->scanning_folder == item->folder) {
2174 res = folder_item_open(item);
2177 if (res == -1 && item->no_select == FALSE) {
2178 main_window_cursor_normal(folderview->mainwin);
2179 STATUSBAR_POP(folderview->mainwin);
2181 alertpanel_error(_("Folder could not be opened."));
2183 folderview->open_folder = FALSE;
2187 } else if (res == -2 && item->no_select == FALSE) {
2188 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2189 data->ctree = ctree;
2191 data->column = column;
2192 data->folderview = folderview;
2193 debug_print("postponing open of %s till end of scan\n",
2194 item->path ? item->path:item->name);
2195 folderview->open_folder = FALSE;
2197 if (folderview->postpone_select_id != 0)
2198 g_source_remove(folderview->postpone_select_id);
2199 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2204 main_window_cursor_normal(folderview->mainwin);
2207 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2208 opened = summary_show(folderview->summaryview, item);
2210 folder_clean_cache_memory(item);
2213 gtkut_ctree_set_focus_row(ctree, old_opened);
2214 gtk_cmctree_select(ctree, old_opened);
2215 folderview->opened = old_opened;
2217 folderview->opened = row;
2218 if (gtk_cmctree_node_is_visible(ctree, row)
2219 != GTK_VISIBILITY_FULL)
2220 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2223 STATUSBAR_POP(folderview->mainwin);
2225 folderview->open_folder = FALSE;
2230 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2231 FolderView *folderview)
2235 item = gtk_cmctree_node_get_row_data(ctree, node);
2236 cm_return_if_fail(item != NULL);
2237 item->collapsed = FALSE;
2238 folderview_update_node(folderview, node);
2241 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2242 FolderView *folderview)
2246 item = gtk_cmctree_node_get_row_data(ctree, node);
2247 cm_return_if_fail(item != NULL);
2248 item->collapsed = TRUE;
2249 folderview_update_node(folderview, node);
2252 static void folderview_popup_close(GtkMenuShell *menu_shell,
2253 FolderView *folderview)
2255 if (!folderview->opened) return;
2257 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2260 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2261 FolderView *folderview)
2263 FolderColumnType type = folderview->col_state[column].type;
2265 prefs_common.folder_col_size[type] = width;
2268 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2270 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2271 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2272 GtkCMCTreeNode *node, *parent_node;
2273 gint *col_pos = folderview->col_pos;
2274 FolderItemUpdateData hookdata;
2276 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2277 if (parent_node == NULL)
2280 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2282 text[col_pos[F_COL_FOLDER]] = item->name;
2283 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2288 gtk_cmctree_expand(ctree, parent_node);
2289 gtk_cmctree_node_set_row_data(ctree, node, item);
2291 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2292 folderview_sort_folders(folderview, parent_node, item->folder);
2294 hookdata.item = item;
2295 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2296 hookdata.msg = NULL;
2297 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2299 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2302 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2304 FolderView *folderview = (FolderView *)data;
2305 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2307 GSList *mlist = NULL;
2309 FolderItem *special_trash = NULL;
2312 if (!folderview->selected) return;
2313 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2314 cm_return_if_fail(item != NULL);
2315 cm_return_if_fail(item->folder != NULL);
2317 if (NULL != (ac = account_find_from_item(item)))
2318 special_trash = account_get_special_folder(ac, F_TRASH);
2320 if (item != item->folder->trash && item != special_trash
2321 && !folder_has_parent_of_type(item, F_TRASH)) return;
2323 if (prefs_common.ask_on_clean) {
2324 if (alertpanel(_("Empty trash"),
2325 _("Delete all messages in trash?"),
2326 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2330 mlist = folder_item_get_msg_list(item);
2332 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2333 MsgInfo * msginfo = (MsgInfo *) cur->data;
2334 if (MSG_IS_LOCKED(msginfo->flags))
2336 /* is it partially received? (partial_recv isn't cached) */
2337 if (msginfo->total_size != 0 &&
2338 msginfo->size != (off_t)msginfo->total_size)
2339 partial_mark_for_delete(msginfo);
2341 procmsg_msg_list_free(mlist);
2343 folder_item_remove_all_msg(item);
2346 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2348 FolderView *folderview = (FolderView *)data;
2349 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2351 FolderItem *special_queue = NULL;
2353 gchar *errstr = NULL;
2355 if (!folderview->selected) return;
2356 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2357 cm_return_if_fail(item != NULL);
2358 cm_return_if_fail(item->folder != NULL);
2360 if (NULL != (ac = account_find_from_item(item)))
2361 special_queue = account_get_special_folder(ac, F_QUEUE);
2363 if (item != item->folder->queue && item != special_queue
2364 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2366 if (procmsg_queue_is_empty(item))
2369 if (prefs_common.work_offline)
2370 if (alertpanel(_("Offline warning"),
2371 _("You're working offline. Override?"),
2372 GTK_STOCK_NO, GTK_STOCK_YES,
2373 NULL) != G_ALERTALTERNATE)
2376 /* ask for confirmation before sending queued messages only
2377 in online mode and if there is at least one message queued
2378 in any of the folder queue
2380 if (prefs_common.confirm_send_queued_messages) {
2381 if (!prefs_common.work_offline) {
2382 if (alertpanel(_("Send queued messages"),
2383 _("Send all queued messages?"),
2384 GTK_STOCK_CANCEL, _("_Send"),
2385 NULL) != G_ALERTALTERNATE)
2390 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2392 alertpanel_error_log(_("Some errors occurred while "
2393 "sending queued messages."));
2395 alertpanel_error_log(_("Some errors occurred "
2396 "while sending queued messages:\n%s"), errstr);
2402 static void folderview_search_cb(GtkAction *action, gpointer data)
2404 FolderView *folderview = (FolderView *)data;
2405 summary_search(folderview->summaryview);
2408 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2410 FolderView *folderview = (FolderView *)data;
2411 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2414 if (!folderview->selected) return;
2416 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2417 cm_return_if_fail(item != NULL);
2418 cm_return_if_fail(item->folder != NULL);
2420 item->processing_pending = TRUE;
2421 folder_item_apply_processing(item);
2422 item->processing_pending = FALSE;
2425 static void folderview_property_cb(GtkAction *action, gpointer data)
2427 FolderView *folderview = (FolderView *)data;
2428 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2431 if (!folderview->selected) return;
2433 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2434 cm_return_if_fail(item != NULL);
2435 cm_return_if_fail(item->folder != NULL);
2437 prefs_folder_item_open(item);
2440 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2442 GSList *list = NULL;
2443 GSList *done = NULL;
2444 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2446 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2447 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2448 && list->data != node) {
2449 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2450 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2453 for (list = done; list != NULL; list = g_slist_next(list)) {
2454 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2460 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2461 FolderItem *to_folder, gboolean copy)
2463 FolderItem *new_folder = NULL;
2467 cm_return_if_fail(folderview != NULL);
2468 cm_return_if_fail(from_folder != NULL);
2469 cm_return_if_fail(to_folder != NULL);
2471 if (prefs_common.warn_dnd) {
2472 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2473 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2474 from_folder->name, to_folder->name);
2475 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2476 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2477 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2480 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2482 else if (status & G_ALERTDISABLE)
2483 prefs_common.warn_dnd = FALSE;
2486 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2487 from_folder->name, to_folder->name);
2488 STATUSBAR_PUSH(folderview->mainwin, buf);
2490 summary_clear_all(folderview->summaryview);
2491 folderview->opened = NULL;
2492 folderview->selected = NULL;
2493 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2495 main_window_cursor_wait(folderview->mainwin);
2497 statusbar_verbosity_set(FALSE);
2498 folder_item_update_freeze();
2499 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2500 statusbar_verbosity_set(FALSE);
2501 main_window_cursor_normal(folderview->mainwin);
2502 STATUSBAR_POP(folderview->mainwin);
2503 folder_item_update_thaw();
2504 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2506 folderview_sort_folders(folderview,
2507 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2508 NULL, to_folder), new_folder->folder);
2509 folderview_select(folderview, new_folder);
2511 statusbar_verbosity_set(FALSE);
2512 main_window_cursor_normal(folderview->mainwin);
2513 STATUSBAR_POP(folderview->mainwin);
2514 folder_item_update_thaw();
2516 case F_MOVE_FAILED_DEST_IS_PARENT:
2517 alertpanel_error(_("Source and destination are the same."));
2519 case F_MOVE_FAILED_DEST_IS_CHILD:
2520 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2521 _("Can't move a folder to one of its children."));
2523 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2524 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2527 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2532 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2535 static gint folderview_clist_compare(GtkCMCList *clist,
2536 gconstpointer ptr1, gconstpointer ptr2)
2538 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2539 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2541 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2543 return item1->order - item2->order;
2546 // if only one folder has an order it comes first
2547 if (item1->order > 0)
2551 if (item2->order > 0)
2557 return (item2->name != NULL);
2561 return g_utf8_collate(item1->name, item2->name);
2564 static void folderview_processing_cb(GtkAction *action, gpointer data)
2566 FolderView *folderview = (FolderView *)data;
2567 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2571 if (!folderview->selected) return;
2573 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2574 cm_return_if_fail(item != NULL);
2575 cm_return_if_fail(item->folder != NULL);
2577 id = folder_item_get_identifier(item);
2578 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2581 prefs_filtering_open(&item->prefs->processing, title,
2582 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2586 void folderview_set_target_folder_color(gint color_op)
2590 FolderView *folderview;
2592 for (list = folderview_list; list != NULL; list = list->next) {
2593 folderview = (FolderView *)list->data;
2594 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2596 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2597 folderview->color_op;
2603 static gchar *last_smallfont = NULL;
2604 static gchar *last_normalfont = NULL;
2605 static gchar *last_boldfont = NULL;
2606 static gboolean last_derive = 0;
2608 void folderview_reinit_fonts(FolderView *folderview)
2611 g_free(last_smallfont);
2612 last_smallfont = NULL;
2613 g_free(last_normalfont);
2614 last_normalfont = NULL;
2615 g_free(last_boldfont);
2616 last_boldfont = NULL;
2619 void folderview_reflect_prefs(void)
2621 gboolean update_font = FALSE;
2622 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2623 FolderItem *item = folderview_get_selected_item(folderview);
2624 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2625 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2626 gint height = gtk_adjustment_get_value(pos);
2628 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2629 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2630 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2631 last_derive != prefs_common.derive_from_normal_font)
2634 g_free(last_smallfont);
2635 last_smallfont = g_strdup(SMALL_FONT);
2636 g_free(last_normalfont);
2637 last_normalfont = g_strdup(NORMAL_FONT);
2638 g_free(last_boldfont);
2639 last_boldfont = g_strdup(BOLD_FONT);
2640 last_derive = prefs_common.derive_from_normal_font;
2643 normal_style = normal_color_style = bold_style =
2644 bold_color_style = bold_tgtfold_style = NULL;
2646 folderview_init(folderview);
2648 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2649 folderview_column_set_titles(folderview);
2650 folderview_set_all();
2652 g_signal_handlers_block_by_func
2653 (G_OBJECT(folderview->ctree),
2654 G_CALLBACK(folderview_selected), folderview);
2657 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2658 GTK_CMCTREE(folderview->ctree), NULL, item);
2660 folderview_select(folderview, item);
2661 folderview->open_folder = FALSE;
2662 folderview->selected = node;
2665 g_signal_handlers_unblock_by_func
2666 (G_OBJECT(folderview->ctree),
2667 G_CALLBACK(folderview_selected), folderview);
2669 pos = gtk_scrolled_window_get_vadjustment(
2670 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2671 gtk_adjustment_set_value(pos, height);
2672 gtk_adjustment_changed(pos);
2673 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2676 static void drag_state_stop(FolderView *folderview)
2678 if (folderview->drag_timer_id)
2679 g_source_remove(folderview->drag_timer_id);
2680 folderview->drag_timer_id = 0;
2681 folderview->drag_node = NULL;
2684 static gboolean folderview_defer_expand(FolderView *folderview)
2686 if (folderview->drag_node) {
2687 folderview_recollapse_nodes(folderview, folderview->drag_node);
2688 if (folderview->drag_item->collapsed) {
2689 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2690 folderview->nodes_to_recollapse = g_slist_append
2691 (folderview->nodes_to_recollapse, folderview->drag_node);
2694 folderview->drag_item = NULL;
2695 folderview->drag_timer_id = 0;
2699 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2701 /* the idea is that we call drag_state_start() whenever we want expansion to
2702 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2703 * we need to call drag_state_stop() */
2704 drag_state_stop(folderview);
2705 /* request expansion */
2706 if (0 != (folderview->drag_timer_id = g_timeout_add
2707 (prefs_common.hover_timeout,
2708 (GSourceFunc)folderview_defer_expand,
2710 folderview->drag_node = node;
2711 folderview->drag_item = item;
2714 #ifndef GENERIC_UMPC
2715 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2716 FolderView *folderview)
2718 GdkDragContext *context;
2720 cm_return_if_fail(folderview != NULL);
2721 if (folderview->selected == NULL) return;
2722 if (folderview->nodes_to_recollapse)
2723 g_slist_free(folderview->nodes_to_recollapse);
2724 folderview->nodes_to_recollapse = NULL;
2725 context = gtk_drag_begin(widget, folderview->target_list,
2726 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2727 gtk_drag_set_icon_default(context);
2730 static void folderview_drag_data_get(GtkWidget *widget,
2731 GdkDragContext *drag_context,
2732 GtkSelectionData *selection_data,
2735 FolderView *folderview)
2739 gchar *source = NULL;
2740 if (info == TARGET_DUMMY) {
2741 sel = GTK_CMCLIST(folderview->ctree)->selection;
2745 item = gtk_cmctree_node_get_row_data
2746 (GTK_CMCTREE(folderview->ctree),
2747 GTK_CMCTREE_NODE(sel->data));
2749 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2750 gtk_selection_data_set(selection_data,
2751 gtk_selection_data_get_target(selection_data), 8,
2752 source, strlen(source));
2755 g_warning("unknown info %d\n", info);
2759 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2761 FolderUpdateData *hookdata;
2762 FolderView *folderview;
2766 folderview = (FolderView *) userdata;
2767 cm_return_val_if_fail(hookdata != NULL, FALSE);
2768 cm_return_val_if_fail(folderview != NULL, FALSE);
2770 ctree = folderview->ctree;
2771 cm_return_val_if_fail(ctree != NULL, FALSE);
2773 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2774 folderview_create_folder_node(folderview, hookdata->item);
2775 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2776 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2777 NULL, folder_item_parent(hookdata->item));
2778 folderview_sort_folders(folderview, node, hookdata->folder);
2779 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2780 GtkCMCTreeNode *node;
2782 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2784 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2785 if (folderview->selected == node)
2786 folderview->selected = NULL;
2787 if (folderview->opened == node)
2788 folderview->opened = NULL;
2790 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2791 /* do nothing, it's done by the ADD and REMOVE) */
2792 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2793 folderview_set(folderview);
2798 static gboolean folderview_dnd_scroll_cb(gpointer data)
2800 FolderView *folderview = (FolderView *)data;
2801 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2802 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2803 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2804 gint max = (int)gtk_adjustment_get_upper(pos) -
2805 (int)gtk_adjustment_get_page_size(pos);
2807 if (folderview->scroll_value == 0) {
2808 folderview->scroll_timeout_id = 0;
2812 if (folderview->scroll_value > 0 && new_val > max) {
2814 } else if (folderview->scroll_value < 0 && new_val < 0) {
2817 gtk_adjustment_set_value(pos, new_val);
2822 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2823 GdkDragContext *context,
2827 FolderView *folderview)
2830 FolderItem *item = NULL, *src_item = NULL;
2831 GtkCMCTreeNode *node = NULL;
2832 gboolean acceptable = FALSE;
2833 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2834 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2835 int height = (int)gtk_adjustment_get_page_size(pos);
2836 int total_height = (int)gtk_adjustment_get_upper(pos);
2837 int vpos = (int)gtk_adjustment_get_value(pos);
2838 int offset = prefs_common.show_col_headers ? 24:0;
2841 if (gtk_cmclist_get_selection_info
2842 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2843 GtkWidget *srcwidget;
2845 if (y > height - (48 - offset) && height + vpos < total_height) {
2846 dist = -(height - (48 - offset) - y);
2847 folderview->scroll_value = 1.41f * (1+(dist / 6));
2848 } else if (y < 72 - (24 - offset) && y >= 0) {
2849 dist = 72 - (24 - offset) - y;
2850 folderview->scroll_value = -1.41f * (1+(dist / 6));
2852 folderview->scroll_value = 0;
2854 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2855 folderview->scroll_timeout_id =
2856 g_timeout_add(30, folderview_dnd_scroll_cb,
2860 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2861 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2862 src_item = folderview->summaryview->folder_item;
2864 srcwidget = gtk_drag_get_source_widget(context);
2865 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2866 /* comes from summaryview */
2867 /* we are copying messages, so only accept folder items that are not
2868 the source item, are no root items and can copy messages */
2869 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2870 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2871 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2873 } else if (srcwidget == folderview->ctree) {
2874 /* comes from folderview */
2875 /* we are moving folder items, only accept folders that are not
2876 the source items and can copy messages and create folder items */
2877 if (item && item->folder && src_item && src_item != item &&
2878 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2879 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2880 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2881 || item->folder == src_item->folder))
2884 /* comes from another app */
2885 /* we are adding messages, so only accept folder items that are
2886 no root items and can copy messages */
2887 if (item && item->folder && folder_item_parent(item) != NULL
2888 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2889 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2894 if (acceptable || (src_item && src_item == item))
2895 drag_state_start(folderview, node, item);
2898 g_signal_handlers_block_by_func
2900 G_CALLBACK(folderview_selected), folderview);
2901 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2902 g_signal_handlers_unblock_by_func
2904 G_CALLBACK(folderview_selected), folderview);
2905 gdk_drag_status(context,
2906 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2907 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2909 if (folderview->opened)
2910 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2911 gdk_drag_status(context, 0, time);
2917 static void folderview_drag_leave_cb(GtkWidget *widget,
2918 GdkDragContext *context,
2920 FolderView *folderview)
2922 drag_state_stop(folderview);
2923 folderview->scroll_value = 0;
2924 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2927 static void free_info (gpointer stuff, gpointer data)
2932 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2933 guint time, FolderItem *item)
2936 GSList *msglist = NULL;
2937 list = uri_list_extract_filenames(data);
2938 if (!(item && item->folder && folder_item_parent(item) != NULL
2939 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2941 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2942 debug_print("item doesn't fit\n");
2946 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2947 debug_print("list is empty\n");
2950 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2951 MsgFileInfo *info = NULL;
2953 if (file_is_email((gchar *)tmp->data)) {
2954 info = g_new0(MsgFileInfo, 1);
2955 info->msginfo = NULL;
2956 info->file = (gchar *)tmp->data;
2957 msglist = g_slist_prepend(msglist, info);
2958 debug_print("file is a mail\n");
2960 debug_print("file isn't a mail\n");
2964 msglist = g_slist_reverse(msglist);
2965 folder_item_add_msgs(item, msglist, FALSE);
2966 g_slist_foreach(msglist, free_info, NULL);
2967 g_slist_free(msglist);
2968 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2970 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2972 list_free_strings(list);
2976 static void folderview_drag_received_cb(GtkWidget *widget,
2977 GdkDragContext *drag_context,
2980 GtkSelectionData *data,
2983 FolderView *folderview)
2986 FolderItem *item = NULL, *src_item;
2987 GtkCMCTreeNode *node;
2988 int offset = prefs_common.show_col_headers ? 24:0;
2990 folderview->scroll_value = 0;
2992 if (info == TARGET_DUMMY) {
2993 drag_state_stop(folderview);
2994 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
2995 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
2996 /* comes from summaryview */
2997 if (gtk_cmclist_get_selection_info
2998 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3001 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3002 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3003 src_item = folderview->summaryview->folder_item;
3005 if (item->no_select) {
3006 alertpanel_error(_("The destination folder can only be used to "
3007 "store subfolders."));
3010 /* re-check (due to acceptable possibly set for folder moves */
3011 if (!(item && item->folder && item->path && !item->no_select &&
3012 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3015 if (item && src_item) {
3016 switch (gdk_drag_context_get_selected_action(drag_context)) {
3017 case GDK_ACTION_COPY:
3018 summary_copy_selected_to(folderview->summaryview, item);
3019 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3021 case GDK_ACTION_MOVE:
3022 case GDK_ACTION_DEFAULT:
3024 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3025 summary_copy_selected_to(folderview->summaryview, item);
3027 summary_move_selected_to(folderview->summaryview, item);
3028 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3031 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3033 /* comes from folderview */
3035 gboolean folder_is_normal = TRUE;
3036 gboolean copy = (GDK_ACTION_COPY ==
3037 gdk_drag_context_get_selected_action(drag_context));
3039 source = (char *)gtk_selection_data_get_data(data) + 17;
3040 if (gtk_cmclist_get_selection_info
3041 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3043 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3046 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3047 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3048 src_item = folder_find_item_from_identifier(source);
3052 src_item->stype == F_NORMAL &&
3053 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3054 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3055 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3056 !folder_has_parent_of_type(src_item, F_TRASH);
3057 if (!item || !src_item || !folder_is_normal) {
3058 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3062 folderview_move_folder(folderview, src_item, item, copy);
3063 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3065 folderview->nodes_to_recollapse = NULL;
3066 } else if (info == TARGET_MAIL_URI_LIST) {
3067 if (gtk_cmclist_get_selection_info
3068 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3071 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3073 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3074 debug_print("no node\n");
3077 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3079 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3080 debug_print("no item\n");
3083 folderview_finish_dnd(gtk_selection_data_get_data(data),
3084 drag_context, time, item);
3088 static void folderview_drag_end_cb(GtkWidget *widget,
3089 GdkDragContext *drag_context,
3090 FolderView *folderview)
3092 drag_state_stop(folderview);
3093 folderview->scroll_value = 0;
3094 g_slist_free(folderview->nodes_to_recollapse);
3095 folderview->nodes_to_recollapse = NULL;
3098 void folderview_register_popup(FolderViewPopup *fpopup)
3102 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3103 FolderView *folderview = folderviews->data;
3104 GtkActionGroup *factory;
3106 factory = create_action_group(folderview, fpopup);
3107 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3109 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3112 void folderview_unregister_popup(FolderViewPopup *fpopup)
3117 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3118 FolderView *folderview = folderviews->data;
3120 g_hash_table_remove(folderview->popups, fpopup->klass);
3122 g_hash_table_remove(folderview_popups, fpopup->klass);