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;
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;
856 folder_item_update_freeze();
857 if (folderview->summaryview->folder_item != item && !recursive)
858 summary_lock(folderview->summaryview);
860 summary_freeze(folderview->summaryview);
863 folderutils_mark_all_read_recursive(item);
865 folderutils_mark_all_read(item);
867 if (folderview->summaryview->folder_item != item && !recursive)
868 summary_unlock(folderview->summaryview);
870 summary_thaw(folderview->summaryview);
871 folder_item_update_thaw();
874 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
876 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
878 cm_return_if_fail(node != NULL);
880 if (folderview->open_folder) {
884 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
885 gtkut_ctree_expand_parent_all(ctree, node);
887 folderview->open_folder = TRUE;
888 gtkut_ctree_set_focus_row(ctree, node);
889 gtk_cmctree_select(ctree, node);
890 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
891 if ((folderview->summaryview->folder_item &&
892 folderview->summaryview->folder_item->total_msgs > 0) ||
893 prefs_common.layout_mode == SMALL_LAYOUT)
894 summary_grab_focus(folderview->summaryview);
896 gtk_widget_grab_focus(folderview->ctree);
899 void folderview_unselect(FolderView *folderview)
901 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
903 (GTK_CMCTREE(folderview->ctree), folderview->opened);
905 folderview->selected = folderview->opened = NULL;
908 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
909 GtkCMCTreeNode *node,
915 node = gtkut_ctree_node_next(ctree, node);
917 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
919 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
920 item = gtk_cmctree_node_get_row_data(ctree, node);
923 if (item->stype == F_TRASH || item->stype == F_DRAFT)
927 if(item->unread_msgs > 0)
931 if(item->new_msgs > 0)
935 if(item->marked_msgs > 0)
944 void folderview_select_next_with_flag(FolderView *folderview,
948 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
949 GtkCMCTreeNode *node = NULL;
950 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
951 gboolean last_open = prefs_common.always_show_msg;
955 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
958 prefs_common.summary_select_prio[0] = ACTION_NEW;
961 prefs_common.summary_select_prio[0] = ACTION_MARKED;
964 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
966 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
968 folderview_select_node(folderview, node);
972 if (!folderview->opened ||
973 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
977 /* search again from the first node */
978 node = folderview_find_next_with_flag(ctree, NULL, flag);
980 folderview_select_node(folderview, node);
983 prefs_common.summary_select_prio[0] = last_summary_select_prio;
984 prefs_common.always_show_msg = last_open;
987 FolderItem *folderview_get_selected_item(FolderView *folderview)
989 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
991 if (!folderview->selected) return NULL;
992 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
995 static void folderview_set_folders(FolderView *folderview)
998 list = folder_get_list();
1000 for (; list != NULL; list = list->next) {
1001 folderview_append_folder(folderview, FOLDER(list->data));
1005 static gchar *get_scan_str(FolderItem *item)
1008 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1009 item->folder->name, G_DIR_SEPARATOR,
1012 return g_strdup_printf(_("Scanning folder %s..."),
1013 item->folder->name);
1015 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1019 for (list = folderview_list; list != NULL; list = list->next) {
1020 FolderView *folderview = (FolderView *)list->data;
1021 MainWindow *mainwin = folderview->mainwin;
1022 gchar *str = get_scan_str(item);
1024 STATUSBAR_PUSH(mainwin, str);
1025 STATUSBAR_POP(mainwin);
1030 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1033 MainWindow *mainwin = mainwindow_get_mainwindow();
1034 FolderView *folderview = NULL;
1035 GtkAdjustment *pos = NULL;
1038 cm_return_if_fail(folder != NULL);
1040 if (!folder->klass->scan_tree) return;
1043 alertpanel_full(_("Rebuild folder tree"),
1044 _("Rebuilding the folder tree will remove "
1045 "local caches. Do you want to continue?"),
1046 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1047 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1048 != G_ALERTALTERNATE) {
1054 window = label_window_create(_("Rebuilding folder tree..."));
1056 window = label_window_create(_("Scanning folder tree..."));
1059 folderview = mainwin->folderview;
1062 pos = gtk_scrolled_window_get_vadjustment(
1063 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1064 height = gtk_adjustment_get_value(pos);
1067 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1068 folder_scan_tree(folder, rebuild);
1069 folder_set_ui_func(folder, NULL, NULL);
1071 folderview_set_all();
1074 pos = gtk_scrolled_window_get_vadjustment(
1075 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1076 gtk_adjustment_set_value(pos, height);
1077 gtk_adjustment_changed(pos);
1079 label_window_destroy(window);
1083 /** folderview_check_new()
1084 * Scan and update the folder and return the
1085 * count the number of new messages since last check.
1086 * \param folder the folder to check for new messages
1087 * \return the number of new messages since last check
1089 gint folderview_check_new(Folder *folder)
1093 FolderView *folderview;
1095 GtkCMCTreeNode *node;
1097 gint former_new_msgs = 0;
1098 gint former_new = 0, former_unread = 0, former_total;
1100 for (list = folderview_list; list != NULL; list = list->next) {
1101 folderview = (FolderView *)list->data;
1102 ctree = GTK_CMCTREE(folderview->ctree);
1103 folderview->scanning_folder = folder;
1105 main_window_lock(folderview->mainwin);
1107 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1108 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1110 item = gtk_cmctree_node_get_row_data(ctree, node);
1111 if (!item || !item->path || !item->folder) continue;
1112 if (item->no_select) continue;
1113 if (folder && folder != item->folder) continue;
1114 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1115 if (!item->prefs->newmailcheck) continue;
1116 if (item->processing_pending == TRUE) {
1117 debug_print("skipping %s, processing pending\n",
1118 item->path ? item->path : item->name);
1121 if (item->scanning != ITEM_NOT_SCANNING) {
1122 debug_print("skipping %s, scanning\n",
1123 item->path ? item->path : item->name);
1127 str = get_scan_str(item);
1129 STATUSBAR_PUSH(folderview->mainwin, str);
1133 folderview_scan_tree_func(item->folder, item, NULL);
1134 former_new = item->new_msgs;
1135 former_unread = item->unread_msgs;
1136 former_total = item->total_msgs;
1138 if (item->folder->klass->scan_required &&
1139 (item->folder->klass->scan_required(item->folder, item) ||
1140 item->folder->inbox == item ||
1141 item->opened == TRUE ||
1142 item->processing_pending == TRUE)) {
1143 if (folder_item_scan(item) < 0) {
1145 summaryview_unlock(folderview->summaryview, item);
1146 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1147 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1148 item->path ? item->path:item->name);
1149 STATUSBAR_POP(folderview->mainwin);
1151 } else if (!FOLDER_IS_LOCAL(folder)) {
1152 STATUSBAR_POP(folderview->mainwin);
1157 } else if (!item->folder->klass->scan_required) {
1158 if (folder_item_scan(item) < 0) {
1159 summaryview_unlock(folderview->summaryview, item);
1160 if (folder && !FOLDER_IS_LOCAL(folder)) {
1161 STATUSBAR_POP(folderview->mainwin);
1166 if (former_new != item->new_msgs ||
1167 former_unread != item->unread_msgs ||
1168 former_total != item->total_msgs)
1169 folderview_update_node(folderview, node);
1171 new_msgs += item->new_msgs;
1172 former_new_msgs += former_new;
1173 STATUSBAR_POP(folderview->mainwin);
1175 folderview->scanning_folder = NULL;
1176 main_window_unlock(folderview->mainwin);
1180 folder_write_list();
1181 /* Number of new messages since last check is the just the difference
1182 * between former_new_msgs and new_msgs. If new_msgs is less than
1183 * former_new_msgs, that would mean another session accessed the folder
1184 * and the result is not well defined.
1186 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1190 void folderview_check_new_all(void)
1194 FolderView *folderview;
1196 folderview = (FolderView *)folderview_list->data;
1199 main_window_lock(folderview->mainwin);
1200 window = label_window_create
1201 (_("Checking for new messages in all folders..."));
1203 list = folder_get_list();
1204 for (; list != NULL; list = list->next) {
1205 Folder *folder = list->data;
1207 folderview_check_new(folder);
1210 folder_write_list();
1211 folderview_set_all();
1213 label_window_destroy(window);
1214 main_window_unlock(folderview->mainwin);
1218 static gboolean folderview_have_children_sub(FolderView *folderview,
1224 if (!item || !item->folder || !item->folder->node)
1227 node = item->folder->node;
1229 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1230 node = node->children;
1232 if (in_sub && item->total_msgs > 0) {
1236 while (node != NULL) {
1237 if (node && node->data) {
1238 FolderItem *next_item = (FolderItem*) node->data;
1240 if (folderview_have_children_sub(folderview,
1249 static gboolean folderview_have_children(FolderView *folderview,
1252 return folderview_have_children_sub(folderview, item, FALSE);
1255 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1261 if (!item || !item->folder || !item->folder->node)
1264 node = item->folder->node;
1266 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1267 node = node->children;
1270 (item->new_msgs > 0 ||
1271 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1275 while (node != NULL) {
1276 if (node && node->data) {
1277 FolderItem *next_item = (FolderItem*) node->data;
1279 if (folderview_have_new_children_sub(folderview,
1288 static gboolean folderview_have_new_children(FolderView *folderview,
1291 return folderview_have_new_children_sub(folderview, item, FALSE);
1294 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1300 if (!item || !item->folder || !item->folder->node)
1303 node = item->folder->node;
1305 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1306 node = node->children;
1309 (item->unread_msgs > 0 ||
1310 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1314 while (node != NULL) {
1315 if (node && node->data) {
1316 FolderItem *next_item = (FolderItem*) node->data;
1318 if (folderview_have_unread_children_sub(folderview,
1328 static gboolean folderview_have_unread_children(FolderView *folderview,
1331 return folderview_have_unread_children_sub(folderview, item, FALSE);
1334 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1340 if (!item || !item->folder || !item->folder->node)
1343 node = item->folder->node;
1345 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1346 node = node->children;
1348 if (in_sub && item->search_match){
1352 while (node != NULL) {
1353 if (node && node->data) {
1354 FolderItem *next_item = (FolderItem*) node->data;
1356 if (folderview_have_matching_children_sub(folderview,
1366 static gboolean folderview_have_matching_children(FolderView *folderview,
1369 return folderview_have_matching_children_sub(folderview, item, FALSE);
1372 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1378 if (!item || !item->folder || !item->folder->node)
1381 node = item->folder->node;
1383 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1384 node = node->children;
1386 if (item->marked_msgs != 0) {
1390 while (node != NULL) {
1391 if (node && node->data) {
1392 FolderItem *next_item = (FolderItem*) node->data;
1394 if (folderview_have_marked_children_sub(folderview,
1403 static gboolean folderview_have_marked_children(FolderView *folderview,
1406 return folderview_have_marked_children_sub(folderview, item, FALSE);
1409 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1411 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1412 GtkStyle *style = NULL;
1413 GtkStyle *color_style = NULL;
1415 GdkPixbuf *xpm, *openxpm;
1416 static GdkPixbuf *searchicon;
1417 gboolean mark = FALSE;
1420 gboolean add_unread_mark;
1421 gboolean add_sub_match_mark;
1422 gboolean use_bold, use_color;
1423 gint *col_pos = folderview->col_pos;
1424 SpecialFolderItemType stype;
1426 item = gtk_cmctree_node_get_row_data(ctree, node);
1427 cm_return_if_fail(item != NULL);
1429 if (!GTK_CMCTREE_ROW(node)->expanded)
1430 mark = folderview_have_marked_children(folderview, item);
1432 mark = (item->marked_msgs != 0);
1434 stype = item->stype;
1435 if (stype == F_NORMAL) {
1436 if (folder_has_parent_of_type(item, F_TRASH))
1438 else if (folder_has_parent_of_type(item, F_DRAFT))
1440 else if (folder_has_parent_of_type(item, F_OUTBOX))
1442 else if (folder_has_parent_of_type(item, F_QUEUE))
1447 if (item->hide_read_msgs || item->hide_read_threads) {
1448 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1449 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1451 xpm = mark?m_inboxxpm:inboxxpm;
1452 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1456 if (item->hide_read_msgs || item->hide_read_threads) {
1457 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1458 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1460 xpm = mark?m_outboxxpm:outboxxpm;
1461 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1465 if (item->hide_read_msgs || item->hide_read_threads) {
1466 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1467 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1469 xpm = mark?m_queuexpm:queuexpm;
1470 openxpm = mark?m_queueopenxpm:queueopenxpm;
1474 if (item->hide_read_msgs || item->hide_read_threads) {
1475 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1476 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1478 xpm = mark?m_trashxpm:trashxpm;
1479 openxpm = mark?m_trashopenxpm:trashopenxpm;
1483 xpm = mark?m_draftsxpm:draftsxpm;
1484 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1487 if (item->hide_read_msgs || item->hide_read_threads) {
1488 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1489 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1491 xpm = mark?m_folderxpm:folderxpm;
1492 openxpm = mark?m_folderopenxpm:folderopenxpm;
1496 if (item->no_select) {
1497 xpm = openxpm = noselectxpm;
1500 name = folder_item_get_name(item);
1502 if (!GTK_CMCTREE_ROW(node)->expanded) {
1503 add_unread_mark = folderview_have_unread_children(
1505 add_sub_match_mark = folderview_have_matching_children(
1508 add_unread_mark = FALSE;
1509 add_sub_match_mark = FALSE;
1512 if (item->search_match) {
1514 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1517 xpm = openxpm = searchicon;
1521 if (prefs_common.display_folder_unread) {
1522 if (folder_has_parent_of_type(item, F_QUEUE)) {
1523 /* only total_msgs matters here */
1524 if (item->total_msgs > 0) {
1525 /* show total number (should be equal to the unread number)
1527 str = g_strdup_printf("%s (%d%s%s)",
1528 name, item->total_msgs,
1529 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1530 (item->unreadmarked_msgs > 0) ? "!" : "");
1533 if (prefs_common.display_folder_unread == 1) {
1534 if (item->unread_msgs > 0) {
1535 /* show unread number and signs */
1536 str = g_strdup_printf("%s (%d%s%s)",
1537 name, item->unread_msgs,
1538 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1539 (item->unreadmarked_msgs > 0) ? "!" : "");
1542 if (item->total_msgs > 0) {
1543 /* show unread number, total number and signs if any */
1544 str = g_strdup_printf("%s (%d/%d%s%s)",
1545 name, item->unread_msgs, item->total_msgs,
1546 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1547 (item->unreadmarked_msgs > 0) ? "!" : "");
1551 if ((str == NULL) &&
1552 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1553 /* no unread/total numbers, but at least one sign */
1554 str = g_strdup_printf("%s (%s%s)",
1556 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1557 (item->unreadmarked_msgs > 0) ? "!" : "");
1561 /* last fallback, folder name only or with +! sign */
1562 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1563 str = g_strdup_printf("%s%s",
1565 } else if (item->unreadmarked_msgs > 0) {
1566 str = g_strdup_printf("%s%s",
1568 } else if (add_sub_match_mark) {
1569 str = g_strdup_printf("%s%s",
1572 str = g_strdup_printf("%s", name);
1575 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1577 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1581 if (!folder_item_parent(item)) {
1582 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1583 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1584 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1586 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1587 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1588 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1591 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1592 folder_has_parent_of_type(item, F_TRASH)) {
1593 use_bold = use_color = FALSE;
1594 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1595 GSList *list = folder_item_get_msg_list(item);
1597 use_bold = use_color = FALSE;
1598 for (cur = list; cur; cur = cur->next) {
1599 MsgInfo *msginfo = (MsgInfo *)cur->data;
1600 if (!MSG_IS_DELETED(msginfo->flags)) {
1601 /* highlight queue folder if there are any messages */
1602 use_bold = use_color = TRUE;
1606 if (!GTK_CMCTREE_ROW(node)->expanded &&
1607 use_bold == FALSE &&
1608 folderview_have_children(folderview, item))
1609 use_bold = use_color = TRUE;
1610 procmsg_msg_list_free(list);
1612 /* if unread messages exist, print with bold font */
1613 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1615 /* if new messages exist, print with colored letter */
1617 (item->new_msgs > 0) ||
1619 folderview_have_new_children(folderview, item));
1622 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1627 if (item->prefs->color > 0 && !use_color) {
1628 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1629 color_style = gtk_style_copy(bold_style);
1630 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1631 style = color_style;
1632 } else if (use_color) {
1633 style = bold_color_style;
1636 if (item->op_count > 0) {
1637 style = bold_tgtfold_style;
1639 } else if (use_color) {
1640 style = normal_color_style;
1641 gtk_cmctree_node_set_foreground(ctree, node,
1642 &folderview->color_new);
1643 } else if (item->op_count > 0) {
1644 style = bold_tgtfold_style;
1645 } else if (item->prefs->color > 0) {
1647 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1648 color_style = gtk_style_copy(normal_style);
1649 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1650 style = color_style;
1652 style = normal_style;
1655 gtk_cmctree_node_set_row_style(ctree, node, style);
1657 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1658 folderview_update_node(folderview, node);
1661 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1664 FolderView *folderview;
1666 GtkCMCTreeNode *node;
1668 cm_return_if_fail(item != NULL);
1670 for (list = folderview_list; list != NULL; list = list->next) {
1671 folderview = (FolderView *)list->data;
1672 ctree = GTK_CMCTREE(folderview->ctree);
1674 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1675 if (node && item->search_match != matches) {
1676 item->search_match = matches;
1677 folderview_update_node(folderview, node);
1682 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1684 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1685 FolderView *folderview = (FolderView *)data;
1687 GtkCMCTreeNode *node;
1688 cm_return_val_if_fail(update_info != NULL, TRUE);
1689 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1690 cm_return_val_if_fail(folderview != NULL, FALSE);
1692 ctree = GTK_CMCTREE(folderview->ctree);
1694 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1697 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1698 folderview_update_node(folderview, node);
1700 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1701 update_info->item == folderview->summaryview->folder_item &&
1702 update_info->item != NULL)
1703 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1704 summary_show(folderview->summaryview, update_info->item);
1710 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1711 GNode *gnode, GtkCMCTreeNode *cnode,
1714 FolderView *folderview = (FolderView *)data;
1715 FolderItem *item = FOLDER_ITEM(gnode->data);
1717 cm_return_val_if_fail(item != NULL, FALSE);
1719 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1720 folderview_update_node(folderview, cnode);
1725 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1728 FolderView *folderview = (FolderView *)data;
1731 if (GTK_CMCTREE_ROW(node)->children) {
1732 item = gtk_cmctree_node_get_row_data(ctree, node);
1733 cm_return_if_fail(item != NULL);
1735 if (!item->collapsed)
1736 gtk_cmctree_expand(ctree, node);
1738 folderview_update_node(folderview, node);
1742 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1743 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1746 GtkCMCTreeNode *node, *parent, *sibling;
1748 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1750 g_warning("%s not found.\n", item->path);
1752 parent = GTK_CMCTREE_ROW(node)->parent;
1753 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1754 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1756 sibling = GTK_CMCTREE_ROW(parent)->children;
1760 tmp = gtk_cmctree_node_get_row_data
1762 if (tmp && tmp->stype != F_NORMAL)
1763 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1767 if (node != sibling)
1768 gtk_cmctree_move(ctree, node, parent, sibling);
1775 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1778 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1779 GtkCMCTreeNode *prev = NULL;
1781 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1782 gtk_sctree_sort_recursive(ctree, root);
1783 if (root && GTK_CMCTREE_ROW(root)->parent) {
1784 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1787 set_special_folder(ctree, folder->inbox, root, &prev);
1788 set_special_folder(ctree, folder->outbox, root, &prev);
1789 set_special_folder(ctree, folder->draft, root, &prev);
1790 set_special_folder(ctree, folder->queue, root, &prev);
1791 set_special_folder(ctree, folder->trash, root, &prev);
1792 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1795 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1797 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1798 GtkCMCTreeNode *root;
1800 cm_return_if_fail(folder != NULL);
1802 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1803 folderview_gnode_func, folderview);
1804 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1806 folderview_sort_folders(folderview, root, folder);
1809 /* callback functions */
1810 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1811 GdkEventButton *event)
1815 FolderViewPopup *fpopup;
1816 GtkActionGroup *action_group;
1818 FolderItem *special_trash = NULL, *special_queue = NULL;
1820 GtkUIManager *ui_manager = gtk_ui_manager_new();
1822 if (folderview->ui_manager)
1823 g_object_unref(folderview->ui_manager);
1825 folderview->ui_manager = ui_manager;
1826 item = folderview_get_selected_item(folderview);
1828 cm_return_if_fail(item != NULL);
1829 cm_return_if_fail(item->folder != NULL);
1830 folder = item->folder;
1832 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1835 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1837 fpopup = g_hash_table_lookup(folderview_popups, "common");
1838 action_group = g_hash_table_lookup(folderview->popups, "common");
1841 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1842 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1843 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1845 if (fpopup->add_menuitems)
1846 fpopup->add_menuitems(ui_manager, item);
1848 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1850 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1851 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1852 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1854 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1856 if (fpopup->set_sensitivity != NULL)
1857 fpopup->set_sensitivity(ui_manager, item);
1859 if (NULL != (ac = account_find_from_item(item))) {
1860 special_trash = account_get_special_folder(ac, F_TRASH);
1861 special_queue = account_get_special_folder(ac, F_QUEUE);
1864 if ((item == folder->trash || item == special_trash
1865 || folder_has_parent_of_type(item, F_TRASH))) {
1866 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1867 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1870 if ((item == folder->queue || item == special_queue
1871 || folder_has_parent_of_type(item, F_QUEUE))) {
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1873 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1876 #define SET_SENS(name, sens) \
1877 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1879 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1880 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1881 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1882 folderview->selected == folderview->opened);
1883 SET_SENS("FolderViewPopup/Properties", TRUE);
1885 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1886 item->total_msgs >= 1 && !item->processing_pending);
1887 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1888 !item->no_select && !item->processing_pending);
1890 if (item == folder->trash || item == special_trash
1891 || folder_has_parent_of_type(item, F_TRASH)) {
1892 GSList *msglist = folder_item_get_msg_list(item);
1893 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1894 procmsg_msg_list_free(msglist);
1896 if (item == folder->queue || item == special_queue
1897 || folder_has_parent_of_type(item, F_QUEUE)) {
1898 GSList *msglist = folder_item_get_msg_list(item);
1899 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1900 procmsg_msg_list_free(msglist);
1904 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1905 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1906 g_signal_connect(G_OBJECT(popup), "selection_done",
1907 G_CALLBACK(folderview_popup_close),
1909 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1910 event->button, event->time);
1913 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1914 FolderView *folderview)
1916 GtkCMCList *clist = GTK_CMCLIST(ctree);
1917 gint prev_row = -1, row = -1, column = -1;
1919 if (!event) return FALSE;
1921 if (event->button == 1 || event->button == 2) {
1922 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1923 folderview->open_folder = TRUE;
1925 if (event->type == GDK_2BUTTON_PRESS) {
1926 if (clist->selection) {
1927 GtkCMCTreeNode *node;
1929 node = GTK_CMCTREE_NODE(clist->selection->data);
1931 gtk_cmctree_toggle_expansion(
1934 folderview->open_folder = FALSE;
1941 if (event->button == 2 || event->button == 3) {
1943 if (clist->selection) {
1944 GtkCMCTreeNode *node;
1946 node = GTK_CMCTREE_NODE(clist->selection->data);
1948 prev_row = gtkut_ctree_get_nth_from_node
1949 (GTK_CMCTREE(ctree), node);
1952 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1955 if (prev_row != row) {
1956 gtk_cmclist_unselect_all(clist);
1957 if (event->button == 2)
1958 folderview_select_node
1960 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1963 gtk_cmclist_select_row(clist, row, column);
1967 if (event->button != 3) return FALSE;
1969 folderview_set_sens_and_popup_menu(folderview, row, event);
1973 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1974 FolderView *folderview)
1976 int row = -1, column = -1;
1978 if (!event) return FALSE;
1980 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1983 if (event->button == 1 && folderview->open_folder == FALSE &&
1984 folderview->opened != NULL) {
1985 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1986 folderview->opened);
1987 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1993 #define BREAK_ON_MODIFIER_KEY() \
1994 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1996 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1997 FolderView *folderview)
1999 if (!event) return FALSE;
2001 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2004 switch (event->keyval) {
2006 #ifndef GENERIC_UMPC
2007 case GDK_KEY_Return:
2008 case GDK_KEY_KP_Enter:
2010 if (folderview->selected) {
2011 folderview_select_node(folderview,
2012 folderview->selected);
2016 case GDK_KEY_Return:
2017 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2018 gtk_cmctree_toggle_expansion(
2019 GTK_CMCTREE(folderview->ctree),
2020 folderview->selected);
2025 BREAK_ON_MODIFIER_KEY();
2026 if (folderview->selected) {
2027 if (folderview->opened == folderview->selected &&
2028 (!folderview->summaryview->folder_item ||
2029 folderview->summaryview->folder_item->total_msgs == 0))
2030 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2032 folderview_select_node(folderview,
2033 folderview->selected);
2043 typedef struct _PostponedSelectData
2046 GtkCMCTreeNode *row;
2048 FolderView *folderview;
2049 } PostponedSelectData;
2051 static gboolean postpone_select(void *data)
2053 PostponedSelectData *psdata = (PostponedSelectData *)data;
2054 debug_print("trying again\n");
2056 psdata->folderview->postpone_select_id = 0;
2057 psdata->folderview->open_folder = TRUE;
2058 main_window_cursor_normal(psdata->folderview->mainwin);
2059 STATUSBAR_POP(psdata->folderview->mainwin);
2060 folderview_selected(psdata->ctree, psdata->row,
2061 psdata->column, psdata->folderview);
2066 void folderview_close_opened(FolderView *folderview)
2068 if (folderview->opened) {
2069 FolderItem *olditem;
2071 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2072 folderview->opened);
2074 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2075 olditem->path ? olditem->path:olditem->name);
2076 /* will be null if we just moved the previously opened folder */
2077 STATUSBAR_PUSH(folderview->mainwin, buf);
2078 main_window_cursor_wait(folderview->mainwin);
2080 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2081 summary_show(folderview->summaryview, NULL);
2082 folder_item_close(olditem);
2083 main_window_cursor_normal(folderview->mainwin);
2084 STATUSBAR_POP(folderview->mainwin);
2085 if (olditem->folder->klass->item_closed)
2086 olditem->folder->klass->item_closed(olditem);
2091 if (folderview->opened &&
2092 !GTK_CMCTREE_ROW(folderview->opened)->children)
2093 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2095 folderview->opened = NULL;
2097 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2098 gint column, FolderView *folderview)
2100 static gboolean can_select = TRUE; /* exclusive lock */
2105 GtkCMCTreeNode *old_opened = folderview->opened;
2107 folderview->selected = row;
2109 debug_print("newly selected %p, opened %p\n", folderview->selected,
2110 folderview->opened);
2111 if (folderview->opened == row) {
2112 folderview->open_folder = FALSE;
2117 item = gtk_cmctree_node_get_row_data(ctree, row);
2120 folderview->open_folder = FALSE;
2124 if (!can_select || summary_is_locked(folderview->summaryview)) {
2125 if (folderview->opened) {
2126 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2127 gtk_cmctree_select(ctree, folderview->opened);
2129 folderview->open_folder = FALSE;
2134 if (!folderview->open_folder) {
2141 /* Save cache for old folder */
2142 /* We don't want to lose all caches if sylpheed crashed */
2143 /* resets folderview->opened to NULL */
2144 folderview_close_opened(folderview);
2146 /* CLAWS: set compose button type: news folder items
2147 * always have a news folder as parent */
2149 toolbar_set_compose_button
2150 (folderview->mainwin->toolbar,
2151 FOLDER_TYPE(item->folder) == F_NEWS ?
2152 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2155 debug_print("Folder %s is selected\n", item->path);
2157 if (!GTK_CMCTREE_ROW(row)->children)
2158 gtk_cmctree_expand(ctree, row);
2160 /* ungrab the mouse event */
2161 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2162 gtk_grab_remove(GTK_WIDGET(ctree));
2163 if (gdk_pointer_is_grabbed())
2164 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2168 /* TODO: wwp: avoid displaying (null) in the status bar */
2169 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2170 item->path : "(null)");
2171 debug_print("%s\n", buf);
2172 STATUSBAR_PUSH(folderview->mainwin, buf);
2175 main_window_cursor_wait(folderview->mainwin);
2177 if (folderview->scanning_folder == item->folder) {
2180 res = folder_item_open(item);
2183 if (res == -1 && item->no_select == FALSE) {
2184 main_window_cursor_normal(folderview->mainwin);
2185 STATUSBAR_POP(folderview->mainwin);
2187 alertpanel_error(_("Folder could not be opened."));
2189 folderview->open_folder = FALSE;
2193 } else if (res == -2 && item->no_select == FALSE) {
2194 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2195 data->ctree = ctree;
2197 data->column = column;
2198 data->folderview = folderview;
2199 debug_print("postponing open of %s till end of scan\n",
2200 item->path ? item->path:item->name);
2201 folderview->open_folder = FALSE;
2203 if (folderview->postpone_select_id != 0)
2204 g_source_remove(folderview->postpone_select_id);
2205 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2210 main_window_cursor_normal(folderview->mainwin);
2213 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2214 opened = summary_show(folderview->summaryview, item);
2216 folder_clean_cache_memory(item);
2219 gtkut_ctree_set_focus_row(ctree, old_opened);
2220 gtk_cmctree_select(ctree, old_opened);
2221 folderview->opened = old_opened;
2223 folderview->opened = row;
2224 if (gtk_cmctree_node_is_visible(ctree, row)
2225 != GTK_VISIBILITY_FULL)
2226 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2229 STATUSBAR_POP(folderview->mainwin);
2231 folderview->open_folder = FALSE;
2236 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2237 FolderView *folderview)
2241 item = gtk_cmctree_node_get_row_data(ctree, node);
2242 cm_return_if_fail(item != NULL);
2243 item->collapsed = FALSE;
2244 folderview_update_node(folderview, node);
2247 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2248 FolderView *folderview)
2252 item = gtk_cmctree_node_get_row_data(ctree, node);
2253 cm_return_if_fail(item != NULL);
2254 item->collapsed = TRUE;
2255 folderview_update_node(folderview, node);
2258 static void folderview_popup_close(GtkMenuShell *menu_shell,
2259 FolderView *folderview)
2261 if (!folderview->opened) return;
2263 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2266 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2267 FolderView *folderview)
2269 FolderColumnType type = folderview->col_state[column].type;
2271 prefs_common.folder_col_size[type] = width;
2274 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2276 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2277 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2278 GtkCMCTreeNode *node, *parent_node;
2279 gint *col_pos = folderview->col_pos;
2280 FolderItemUpdateData hookdata;
2282 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2283 if (parent_node == NULL)
2286 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2288 text[col_pos[F_COL_FOLDER]] = item->name;
2289 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2294 gtk_cmctree_expand(ctree, parent_node);
2295 gtk_cmctree_node_set_row_data(ctree, node, item);
2297 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2298 folderview_sort_folders(folderview, parent_node, item->folder);
2300 hookdata.item = item;
2301 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2302 hookdata.msg = NULL;
2303 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2305 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2308 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2310 FolderView *folderview = (FolderView *)data;
2311 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2313 GSList *mlist = NULL;
2315 FolderItem *special_trash = NULL;
2318 if (!folderview->selected) return;
2319 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2320 cm_return_if_fail(item != NULL);
2321 cm_return_if_fail(item->folder != NULL);
2323 if (NULL != (ac = account_find_from_item(item)))
2324 special_trash = account_get_special_folder(ac, F_TRASH);
2326 if (item != item->folder->trash && item != special_trash
2327 && !folder_has_parent_of_type(item, F_TRASH)) return;
2329 if (prefs_common.ask_on_clean) {
2330 if (alertpanel(_("Empty trash"),
2331 _("Delete all messages in trash?"),
2332 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2336 mlist = folder_item_get_msg_list(item);
2338 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2339 MsgInfo * msginfo = (MsgInfo *) cur->data;
2340 if (MSG_IS_LOCKED(msginfo->flags))
2342 /* is it partially received? (partial_recv isn't cached) */
2343 if (msginfo->total_size != 0 &&
2344 msginfo->size != (off_t)msginfo->total_size)
2345 partial_mark_for_delete(msginfo);
2347 procmsg_msg_list_free(mlist);
2349 folder_item_remove_all_msg(item);
2352 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2354 FolderView *folderview = (FolderView *)data;
2355 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2357 FolderItem *special_queue = NULL;
2359 gchar *errstr = NULL;
2361 if (!folderview->selected) return;
2362 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2363 cm_return_if_fail(item != NULL);
2364 cm_return_if_fail(item->folder != NULL);
2366 if (NULL != (ac = account_find_from_item(item)))
2367 special_queue = account_get_special_folder(ac, F_QUEUE);
2369 if (item != item->folder->queue && item != special_queue
2370 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2372 if (procmsg_queue_is_empty(item))
2375 if (prefs_common.work_offline)
2376 if (alertpanel(_("Offline warning"),
2377 _("You're working offline. Override?"),
2378 GTK_STOCK_NO, GTK_STOCK_YES,
2379 NULL) != G_ALERTALTERNATE)
2382 /* ask for confirmation before sending queued messages only
2383 in online mode and if there is at least one message queued
2384 in any of the folder queue
2386 if (prefs_common.confirm_send_queued_messages) {
2387 if (!prefs_common.work_offline) {
2388 if (alertpanel(_("Send queued messages"),
2389 _("Send all queued messages?"),
2390 GTK_STOCK_CANCEL, _("_Send"),
2391 NULL) != G_ALERTALTERNATE)
2396 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2398 alertpanel_error_log(_("Some errors occurred while "
2399 "sending queued messages."));
2401 alertpanel_error_log(_("Some errors occurred "
2402 "while sending queued messages:\n%s"), errstr);
2408 static void folderview_search_cb(GtkAction *action, gpointer data)
2410 FolderView *folderview = (FolderView *)data;
2411 summary_search(folderview->summaryview);
2414 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2416 FolderView *folderview = (FolderView *)data;
2417 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2420 if (!folderview->selected) return;
2422 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2423 cm_return_if_fail(item != NULL);
2424 cm_return_if_fail(item->folder != NULL);
2426 item->processing_pending = TRUE;
2427 folder_item_apply_processing(item);
2428 item->processing_pending = FALSE;
2431 static void folderview_property_cb(GtkAction *action, gpointer data)
2433 FolderView *folderview = (FolderView *)data;
2434 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2437 if (!folderview->selected) return;
2439 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2440 cm_return_if_fail(item != NULL);
2441 cm_return_if_fail(item->folder != NULL);
2443 prefs_folder_item_open(item);
2446 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2448 GSList *list = NULL;
2449 GSList *done = NULL;
2450 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2452 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2453 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2454 && list->data != node) {
2455 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2456 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2459 for (list = done; list != NULL; list = g_slist_next(list)) {
2460 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2466 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2467 FolderItem *to_folder, gboolean copy)
2469 FolderItem *new_folder = NULL;
2473 cm_return_if_fail(folderview != NULL);
2474 cm_return_if_fail(from_folder != NULL);
2475 cm_return_if_fail(to_folder != NULL);
2477 if (prefs_common.warn_dnd) {
2478 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2479 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2480 from_folder->name, to_folder->name);
2481 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2482 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2483 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2486 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2488 else if (status & G_ALERTDISABLE)
2489 prefs_common.warn_dnd = FALSE;
2492 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2493 from_folder->name, to_folder->name);
2494 STATUSBAR_PUSH(folderview->mainwin, buf);
2496 summary_clear_all(folderview->summaryview);
2497 folderview->opened = NULL;
2498 folderview->selected = NULL;
2499 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2501 main_window_cursor_wait(folderview->mainwin);
2503 statusbar_verbosity_set(FALSE);
2504 folder_item_update_freeze();
2505 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2506 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2507 statusbar_verbosity_set(FALSE);
2508 main_window_cursor_normal(folderview->mainwin);
2509 STATUSBAR_POP(folderview->mainwin);
2510 folder_item_update_thaw();
2511 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2513 folderview_sort_folders(folderview,
2514 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2515 NULL, to_folder), new_folder->folder);
2516 folderview_select(folderview, new_folder);
2517 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2519 statusbar_verbosity_set(FALSE);
2520 main_window_cursor_normal(folderview->mainwin);
2521 STATUSBAR_POP(folderview->mainwin);
2522 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2523 folder_item_update_thaw();
2525 case F_MOVE_FAILED_DEST_IS_PARENT:
2526 alertpanel_error(_("Source and destination are the same."));
2528 case F_MOVE_FAILED_DEST_IS_CHILD:
2529 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2530 _("Can't move a folder to one of its children."));
2532 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2533 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2536 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2541 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2544 static gint folderview_clist_compare(GtkCMCList *clist,
2545 gconstpointer ptr1, gconstpointer ptr2)
2547 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2548 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2550 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2552 return item1->order - item2->order;
2555 // if only one folder has an order it comes first
2556 if (item1->order > 0)
2560 if (item2->order > 0)
2566 return (item2->name != NULL);
2570 return g_utf8_collate(item1->name, item2->name);
2573 static void folderview_processing_cb(GtkAction *action, gpointer data)
2575 FolderView *folderview = (FolderView *)data;
2576 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2580 if (!folderview->selected) return;
2582 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2583 cm_return_if_fail(item != NULL);
2584 cm_return_if_fail(item->folder != NULL);
2586 id = folder_item_get_identifier(item);
2587 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2590 prefs_filtering_open(&item->prefs->processing, title,
2591 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2595 void folderview_set_target_folder_color(gint color_op)
2599 FolderView *folderview;
2601 for (list = folderview_list; list != NULL; list = list->next) {
2602 folderview = (FolderView *)list->data;
2603 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2605 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2606 folderview->color_op;
2612 static gchar *last_smallfont = NULL;
2613 static gchar *last_normalfont = NULL;
2614 static gchar *last_boldfont = NULL;
2615 static gboolean last_derive = 0;
2617 void folderview_reinit_fonts(FolderView *folderview)
2620 g_free(last_smallfont);
2621 last_smallfont = NULL;
2622 g_free(last_normalfont);
2623 last_normalfont = NULL;
2624 g_free(last_boldfont);
2625 last_boldfont = NULL;
2628 void folderview_reflect_prefs(void)
2630 gboolean update_font = FALSE;
2631 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2632 FolderItem *item = folderview_get_selected_item(folderview);
2633 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2634 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2635 gint height = gtk_adjustment_get_value(pos);
2637 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2638 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2639 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2640 last_derive != prefs_common.derive_from_normal_font)
2643 g_free(last_smallfont);
2644 last_smallfont = g_strdup(SMALL_FONT);
2645 g_free(last_normalfont);
2646 last_normalfont = g_strdup(NORMAL_FONT);
2647 g_free(last_boldfont);
2648 last_boldfont = g_strdup(BOLD_FONT);
2649 last_derive = prefs_common.derive_from_normal_font;
2652 normal_style = normal_color_style = bold_style =
2653 bold_color_style = bold_tgtfold_style = NULL;
2655 folderview_init(folderview);
2657 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2658 folderview_column_set_titles(folderview);
2659 folderview_set_all();
2661 g_signal_handlers_block_by_func
2662 (G_OBJECT(folderview->ctree),
2663 G_CALLBACK(folderview_selected), folderview);
2666 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2667 GTK_CMCTREE(folderview->ctree), NULL, item);
2669 folderview_select(folderview, item);
2670 folderview->open_folder = FALSE;
2671 folderview->selected = node;
2674 g_signal_handlers_unblock_by_func
2675 (G_OBJECT(folderview->ctree),
2676 G_CALLBACK(folderview_selected), folderview);
2678 pos = gtk_scrolled_window_get_vadjustment(
2679 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2680 gtk_adjustment_set_value(pos, height);
2681 gtk_adjustment_changed(pos);
2682 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2685 static void drag_state_stop(FolderView *folderview)
2687 if (folderview->drag_timer_id)
2688 g_source_remove(folderview->drag_timer_id);
2689 folderview->drag_timer_id = 0;
2690 folderview->drag_node = NULL;
2693 static gboolean folderview_defer_expand(FolderView *folderview)
2695 if (folderview->drag_node) {
2696 folderview_recollapse_nodes(folderview, folderview->drag_node);
2697 if (folderview->drag_item->collapsed) {
2698 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2699 folderview->nodes_to_recollapse = g_slist_append
2700 (folderview->nodes_to_recollapse, folderview->drag_node);
2703 folderview->drag_item = NULL;
2704 folderview->drag_timer_id = 0;
2708 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2710 /* the idea is that we call drag_state_start() whenever we want expansion to
2711 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2712 * we need to call drag_state_stop() */
2713 drag_state_stop(folderview);
2714 /* request expansion */
2715 if (0 != (folderview->drag_timer_id = g_timeout_add
2716 (prefs_common.hover_timeout,
2717 (GSourceFunc)folderview_defer_expand,
2719 folderview->drag_node = node;
2720 folderview->drag_item = item;
2723 #ifndef GENERIC_UMPC
2724 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2725 FolderView *folderview)
2727 GdkDragContext *context;
2729 cm_return_if_fail(folderview != NULL);
2730 if (folderview->selected == NULL) return;
2731 if (folderview->nodes_to_recollapse)
2732 g_slist_free(folderview->nodes_to_recollapse);
2733 folderview->nodes_to_recollapse = NULL;
2734 context = gtk_drag_begin(widget, folderview->target_list,
2735 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2736 gtk_drag_set_icon_default(context);
2739 static void folderview_drag_data_get(GtkWidget *widget,
2740 GdkDragContext *drag_context,
2741 GtkSelectionData *selection_data,
2744 FolderView *folderview)
2748 gchar *source = NULL;
2749 if (info == TARGET_DUMMY) {
2750 sel = GTK_CMCLIST(folderview->ctree)->selection;
2754 item = gtk_cmctree_node_get_row_data
2755 (GTK_CMCTREE(folderview->ctree),
2756 GTK_CMCTREE_NODE(sel->data));
2758 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2759 gtk_selection_data_set(selection_data,
2760 gtk_selection_data_get_target(selection_data), 8,
2761 source, strlen(source));
2764 g_warning("unknown info %d\n", info);
2768 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2770 FolderUpdateData *hookdata;
2771 FolderView *folderview;
2775 folderview = (FolderView *) userdata;
2776 cm_return_val_if_fail(hookdata != NULL, FALSE);
2777 cm_return_val_if_fail(folderview != NULL, FALSE);
2779 ctree = folderview->ctree;
2780 cm_return_val_if_fail(ctree != NULL, FALSE);
2782 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2783 folderview_create_folder_node(folderview, hookdata->item);
2784 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2785 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2786 NULL, folder_item_parent(hookdata->item));
2787 folderview_sort_folders(folderview, node, hookdata->folder);
2788 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2789 GtkCMCTreeNode *node;
2791 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2793 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2794 if (folderview->selected == node)
2795 folderview->selected = NULL;
2796 if (folderview->opened == node)
2797 folderview->opened = NULL;
2799 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2800 /* do nothing, it's done by the ADD and REMOVE) */
2801 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2802 folderview_set(folderview);
2807 static gboolean folderview_dnd_scroll_cb(gpointer data)
2809 FolderView *folderview = (FolderView *)data;
2810 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2811 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2812 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2813 gint max = (int)gtk_adjustment_get_upper(pos) -
2814 (int)gtk_adjustment_get_page_size(pos);
2816 if (folderview->scroll_value == 0) {
2817 folderview->scroll_timeout_id = 0;
2821 if (folderview->scroll_value > 0 && new_val > max) {
2823 } else if (folderview->scroll_value < 0 && new_val < 0) {
2826 gtk_adjustment_set_value(pos, new_val);
2831 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2832 GdkDragContext *context,
2836 FolderView *folderview)
2839 FolderItem *item = NULL, *src_item = NULL;
2840 GtkCMCTreeNode *node = NULL;
2841 gboolean acceptable = FALSE;
2842 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2843 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2844 int height = (int)gtk_adjustment_get_page_size(pos);
2845 int total_height = (int)gtk_adjustment_get_upper(pos);
2846 int vpos = (int)gtk_adjustment_get_value(pos);
2847 int offset = prefs_common.show_col_headers ? 24:0;
2850 if (gtk_cmclist_get_selection_info
2851 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2852 GtkWidget *srcwidget;
2854 if (y > height - (48 - offset) && height + vpos < total_height) {
2855 dist = -(height - (48 - offset) - y);
2856 folderview->scroll_value = 1.41f * (1+(dist / 6));
2857 } else if (y < 72 - (24 - offset) && y >= 0) {
2858 dist = 72 - (24 - offset) - y;
2859 folderview->scroll_value = -1.41f * (1+(dist / 6));
2861 folderview->scroll_value = 0;
2863 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2864 folderview->scroll_timeout_id =
2865 g_timeout_add(30, folderview_dnd_scroll_cb,
2869 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2870 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2871 src_item = folderview->summaryview->folder_item;
2873 srcwidget = gtk_drag_get_source_widget(context);
2874 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2875 /* comes from summaryview */
2876 /* we are copying messages, so only accept folder items that are not
2877 the source item, are no root items and can copy messages */
2878 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2879 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2880 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2882 } else if (srcwidget == folderview->ctree) {
2883 /* comes from folderview */
2884 /* we are moving folder items, only accept folders that are not
2885 the source items and can copy messages and create folder items */
2886 if (item && item->folder && src_item && src_item != item &&
2887 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2888 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2889 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2890 || item->folder == src_item->folder))
2893 /* comes from another app */
2894 /* we are adding messages, so only accept folder items that are
2895 no root items and can copy messages */
2896 if (item && item->folder && folder_item_parent(item) != NULL
2897 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2898 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2903 if (acceptable || (src_item && src_item == item))
2904 drag_state_start(folderview, node, item);
2907 g_signal_handlers_block_by_func
2909 G_CALLBACK(folderview_selected), folderview);
2910 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2911 g_signal_handlers_unblock_by_func
2913 G_CALLBACK(folderview_selected), folderview);
2914 gdk_drag_status(context,
2915 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2916 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2918 if (folderview->opened)
2919 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2920 gdk_drag_status(context, 0, time);
2926 static void folderview_drag_leave_cb(GtkWidget *widget,
2927 GdkDragContext *context,
2929 FolderView *folderview)
2931 drag_state_stop(folderview);
2932 folderview->scroll_value = 0;
2933 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2936 static void free_info (gpointer stuff, gpointer data)
2941 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2942 guint time, FolderItem *item)
2945 GSList *msglist = NULL;
2946 list = uri_list_extract_filenames(data);
2947 if (!(item && item->folder && folder_item_parent(item) != NULL
2948 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2950 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2951 debug_print("item doesn't fit\n");
2955 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2956 debug_print("list is empty\n");
2959 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2960 MsgFileInfo *info = NULL;
2962 if (file_is_email((gchar *)tmp->data)) {
2963 info = g_new0(MsgFileInfo, 1);
2964 info->msginfo = NULL;
2965 info->file = (gchar *)tmp->data;
2966 msglist = g_slist_prepend(msglist, info);
2967 debug_print("file is a mail\n");
2969 debug_print("file isn't a mail\n");
2973 msglist = g_slist_reverse(msglist);
2974 folder_item_add_msgs(item, msglist, FALSE);
2975 g_slist_foreach(msglist, free_info, NULL);
2976 g_slist_free(msglist);
2977 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2979 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2981 list_free_strings(list);
2985 static void folderview_drag_received_cb(GtkWidget *widget,
2986 GdkDragContext *drag_context,
2989 GtkSelectionData *data,
2992 FolderView *folderview)
2995 FolderItem *item = NULL, *src_item;
2996 GtkCMCTreeNode *node;
2997 int offset = prefs_common.show_col_headers ? 24:0;
2999 folderview->scroll_value = 0;
3001 if (info == TARGET_DUMMY) {
3002 drag_state_stop(folderview);
3003 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3004 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3005 /* comes from summaryview */
3006 if (gtk_cmclist_get_selection_info
3007 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3010 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3011 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3012 src_item = folderview->summaryview->folder_item;
3014 if (item->no_select) {
3015 alertpanel_error(_("The destination folder can only be used to "
3016 "store subfolders."));
3019 /* re-check (due to acceptable possibly set for folder moves */
3020 if (!(item && item->folder && item->path && !item->no_select &&
3021 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3024 if (item && src_item) {
3025 switch (gdk_drag_context_get_selected_action(drag_context)) {
3026 case GDK_ACTION_COPY:
3027 summary_copy_selected_to(folderview->summaryview, item);
3028 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3030 case GDK_ACTION_MOVE:
3031 case GDK_ACTION_DEFAULT:
3033 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3034 summary_copy_selected_to(folderview->summaryview, item);
3036 summary_move_selected_to(folderview->summaryview, item);
3037 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3040 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3042 /* comes from folderview */
3044 gboolean folder_is_normal = TRUE;
3045 gboolean copy = (GDK_ACTION_COPY ==
3046 gdk_drag_context_get_selected_action(drag_context));
3048 source = (char *)gtk_selection_data_get_data(data) + 17;
3049 if (gtk_cmclist_get_selection_info
3050 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3052 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3055 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3056 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3057 src_item = folder_find_item_from_identifier(source);
3061 src_item->stype == F_NORMAL &&
3062 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3063 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3064 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3065 !folder_has_parent_of_type(src_item, F_TRASH);
3066 if (!item || !src_item || !folder_is_normal) {
3067 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3071 folderview_move_folder(folderview, src_item, item, copy);
3072 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3074 folderview->nodes_to_recollapse = NULL;
3075 } else if (info == TARGET_MAIL_URI_LIST) {
3076 if (gtk_cmclist_get_selection_info
3077 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3080 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3082 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3083 debug_print("no node\n");
3086 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3088 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3089 debug_print("no item\n");
3092 folderview_finish_dnd(gtk_selection_data_get_data(data),
3093 drag_context, time, item);
3097 static void folderview_drag_end_cb(GtkWidget *widget,
3098 GdkDragContext *drag_context,
3099 FolderView *folderview)
3101 drag_state_stop(folderview);
3102 folderview->scroll_value = 0;
3103 g_slist_free(folderview->nodes_to_recollapse);
3104 folderview->nodes_to_recollapse = NULL;
3107 void folderview_register_popup(FolderViewPopup *fpopup)
3111 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3112 FolderView *folderview = folderviews->data;
3113 GtkActionGroup *factory;
3115 factory = create_action_group(folderview, fpopup);
3116 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3118 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3121 void folderview_unregister_popup(FolderViewPopup *fpopup)
3126 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3127 FolderView *folderview = folderviews->data;
3129 g_hash_table_remove(folderview->popups, fpopup->klass);
3131 g_hash_table_remove(folderview_popups, fpopup->klass);