2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_handler (GtkAction *action,
170 static void mark_all_read_cb (GtkAction *action,
172 static void mark_all_read_recursive_cb (GtkAction *action,
175 static void folderview_empty_trash_cb (GtkAction *action,
178 static void folderview_send_queue_cb (GtkAction *action,
181 static void folderview_search_cb (GtkAction *action,
183 static void folderview_run_processing_cb(GtkAction *action,
186 static void folderview_property_cb (GtkAction *action,
189 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
190 GdkDragContext *context,
194 FolderView *folderview);
195 static void folderview_drag_leave_cb (GtkWidget *widget,
196 GdkDragContext *context,
198 FolderView *folderview);
199 static void folderview_drag_received_cb (GtkWidget *widget,
200 GdkDragContext *drag_context,
203 GtkSelectionData *data,
206 FolderView *folderview);
208 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
209 FolderView *folderview);
211 static void folderview_drag_data_get (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 GtkSelectionData *selection_data,
216 FolderView *folderview);
217 static void folderview_drag_end_cb (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 FolderView *folderview);
221 static void folderview_create_folder_node (FolderView *folderview,
223 static gboolean folderview_update_folder (gpointer source,
225 static gboolean folderview_update_item_claws (gpointer source,
227 static void folderview_processing_cb(GtkAction *action, gpointer data);
228 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
229 GdkEventButton *event);
231 GHashTable *folderview_popups;
233 static GtkActionEntry folderview_common_popup_entries[] =
235 {"FolderViewPopup", NULL, "FolderViewPopup" },
236 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
237 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
238 {"FolderViewPopup/---", NULL, "---" },
239 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
240 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
241 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
242 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
243 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
244 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
248 GtkTargetEntry folderview_drag_types[] =
250 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
251 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
254 void folderview_initialize(void)
256 FolderViewPopup *fpopup;
258 fpopup = g_new0(FolderViewPopup, 1);
260 fpopup->klass = "common";
261 fpopup->path = "<CommonFolder>";
262 fpopup->entries = folderview_common_popup_entries;
263 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
264 fpopup->set_sensitivity = NULL;
266 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
267 g_hash_table_insert(folderview_popups, "common", fpopup);
270 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
272 FolderViewPopup *fpopup_common;
273 GtkActionGroup *action_group;
275 action_group = cm_menu_create_action_group(
277 fpopup->entries, fpopup->n_entries,
278 (gpointer)folderview);
280 if (fpopup->toggle_entries)
281 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
282 fpopup->n_toggle_entries,
283 (gpointer)folderview);
284 if (fpopup->radio_entries)
285 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
286 fpopup->n_radio_entries, fpopup->radio_default,
287 G_CALLBACK(fpopup->radio_callback),
288 (gpointer)folderview);
290 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
291 if (fpopup_common != fpopup) {
292 gtk_action_group_add_actions(action_group, fpopup_common->entries,
293 fpopup_common->n_entries,
294 (gpointer)folderview);
295 if (fpopup_common->toggle_entries)
296 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
297 fpopup_common->n_toggle_entries,
298 (gpointer)folderview);
299 if (fpopup_common->radio_entries)
300 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
301 fpopup_common->n_radio_entries, fpopup_common->radio_default,
302 G_CALLBACK(fpopup_common->radio_callback),
303 (gpointer)folderview);
309 static void create_action_groups(gpointer key, gpointer value, gpointer data)
311 FolderView *folderview = data;
312 FolderViewPopup *fpopup = value;
313 GtkActionGroup *group;
315 group = create_action_group(folderview, fpopup);
316 g_hash_table_insert(folderview->popups, fpopup->klass, group);
319 static void folderview_column_set_titles(FolderView *folderview)
321 GtkWidget *ctree = folderview->ctree;
322 GtkWidget *label_folder;
323 GtkWidget *label_new;
324 GtkWidget *label_unread;
325 GtkWidget *label_total;
326 GtkWidget *hbox_folder;
328 GtkWidget *hbox_unread;
329 GtkWidget *hbox_total;
330 gint *col_pos = folderview->col_pos;
332 debug_print("setting titles...\n");
333 gtk_widget_realize(folderview->ctree);
334 gtk_widget_show_all(folderview->scrolledwin);
336 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
337 * instead text (text overflows making them unreadable and ugly) */
338 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
340 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
342 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
345 label_folder = gtk_label_new(_("Folder"));
346 label_new = gtk_image_new_from_pixbuf(newxpm);
347 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
348 label_total = gtk_image_new_from_pixbuf(readxpm);
350 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
352 hbox_folder = gtk_hbox_new(FALSE, 4);
353 hbox_new = gtk_hbox_new(FALSE, 4);
354 hbox_unread = gtk_hbox_new(FALSE, 4);
355 hbox_total = gtk_hbox_new(FALSE, 4);
358 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
359 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
360 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
361 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
362 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
363 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
364 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
365 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
367 gtk_widget_show_all(hbox_folder);
368 gtk_widget_show_all(hbox_new);
369 gtk_widget_show_all(hbox_unread);
370 gtk_widget_show_all(hbox_total);
373 gtk_widget_set_size_request(hbox_new, -1, 20);
374 gtk_widget_set_size_request(hbox_unread, -1, 20);
375 gtk_widget_set_size_request(hbox_total, -1, 20);
378 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
379 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
380 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
381 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
387 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
388 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
389 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
392 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
394 FolderView *folderview = (FolderView *)data;
395 GdkEventButton event;
396 if (folderview_get_selected_item(folderview) == NULL)
400 event.time = gtk_get_current_event_time();
402 folderview_set_sens_and_popup_menu(folderview, -1,
409 static GtkWidget *folderview_ctree_create(FolderView *folderview)
413 FolderColumnState *col_state;
414 FolderColumnType type;
415 gchar *titles[N_FOLDER_COLS];
417 GtkWidget *scrolledwin = folderview->scrolledwin;
419 debug_print("creating tree...\n");
420 memset(titles, 0, sizeof(titles));
422 col_state = prefs_folder_column_get_config();
423 memset(titles, 0, sizeof(titles));
425 col_pos = folderview->col_pos;
427 for (i = 0; i < N_FOLDER_COLS; i++) {
428 folderview->col_state[i] = col_state[i];
429 type = col_state[i].type;
433 titles[col_pos[F_COL_FOLDER]] = _("Folder");
434 titles[col_pos[F_COL_NEW]] = _("New");
435 titles[col_pos[F_COL_UNREAD]] = _("Unread");
436 /* TRANSLATORS: This in Number sign in American style */
437 titles[col_pos[F_COL_TOTAL]] = _("#");
439 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
442 if (prefs_common.show_col_headers == FALSE)
443 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
446 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
447 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
449 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
450 col_pos[F_COL_UNREAD],
452 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
453 col_pos[F_COL_TOTAL],
455 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
456 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
457 GTK_CMCTREE_EXPANDER_TRIANGLE);
459 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
460 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
462 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
463 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
465 /* don't let title buttons take key focus */
466 for (i = 0; i < N_FOLDER_COLS; i++) {
467 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
468 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
469 prefs_common.folder_col_size[i]);
470 gtk_cmclist_set_column_visibility
471 (GTK_CMCLIST(ctree), i, col_state[i].visible);
474 g_signal_connect(G_OBJECT(ctree), "key_press_event",
475 G_CALLBACK(folderview_key_pressed),
477 g_signal_connect(G_OBJECT(ctree), "button_press_event",
478 G_CALLBACK(folderview_button_pressed),
480 g_signal_connect(G_OBJECT(ctree), "popup-menu",
481 G_CALLBACK(folderview_popup_menu), folderview);
482 g_signal_connect(G_OBJECT(ctree), "button_release_event",
483 G_CALLBACK(folderview_button_released),
485 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
486 G_CALLBACK(folderview_selected), folderview);
488 /* drag-n-dropping folders on maemo is impractical as this
489 * opens the folder almost everytime */
490 g_signal_connect(G_OBJECT(ctree), "start_drag",
491 G_CALLBACK(folderview_start_drag), folderview);
493 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
494 G_CALLBACK(folderview_drag_data_get),
497 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
498 G_CALLBACK(folderview_tree_expanded),
500 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
501 G_CALLBACK(folderview_tree_collapsed),
504 g_signal_connect(G_OBJECT(ctree), "resize_column",
505 G_CALLBACK(folderview_col_resized),
509 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
510 folderview_drag_types, 2,
511 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
512 g_signal_connect(G_OBJECT(ctree), "drag_motion",
513 G_CALLBACK(folderview_drag_motion_cb),
515 g_signal_connect(G_OBJECT(ctree), "drag_leave",
516 G_CALLBACK(folderview_drag_leave_cb),
518 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
519 G_CALLBACK(folderview_drag_received_cb),
521 g_signal_connect(G_OBJECT(ctree), "drag_end",
522 G_CALLBACK(folderview_drag_end_cb),
525 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
530 void folderview_set_column_order(FolderView *folderview)
532 GtkWidget *ctree = folderview->ctree;
533 FolderItem *item = folderview_get_selected_item(folderview);
534 FolderItem *sel_item = NULL, *op_item = NULL;
535 GtkWidget *scrolledwin = folderview->scrolledwin;
537 if (folderview->drag_timer_id != 0) {
538 g_source_remove(folderview->drag_timer_id);
539 folderview->drag_timer_id = 0;
541 if (folderview->deferred_refresh_id != 0) {
542 g_source_remove(folderview->deferred_refresh_id);
543 folderview->deferred_refresh_id = 0;
545 if (folderview->scroll_timeout_id != 0) {
546 g_source_remove(folderview->scroll_timeout_id);
547 folderview->scroll_timeout_id = 0;
549 if (folderview->postpone_select_id != 0) {
550 g_source_remove(folderview->postpone_select_id);
551 folderview->postpone_select_id = 0;
554 if (folderview->selected)
555 sel_item = folderview_get_selected_item(folderview);
556 if (folderview->opened)
557 op_item = folderview_get_opened_item(folderview);
559 debug_print("recreating tree...\n");
560 gtk_widget_destroy(folderview->ctree);
563 folderview->ctree = ctree = folderview_ctree_create(folderview);
564 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
565 GTK_CMCLIST(ctree)->hadjustment);
566 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
567 GTK_CMCLIST(ctree)->vadjustment);
568 gtk_widget_show(ctree);
571 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
573 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
575 folderview_set(folderview);
576 folderview_column_set_titles(folderview);
578 folderview_select(folderview,item);
581 FolderView *folderview_create(void)
583 FolderView *folderview;
584 GtkWidget *scrolledwin;
587 debug_print("Creating folder view...\n");
588 folderview = g_new0(FolderView, 1);
590 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
591 gtk_scrolled_window_set_policy
592 (GTK_SCROLLED_WINDOW(scrolledwin),
593 GTK_POLICY_AUTOMATIC,
594 prefs_common.folderview_vscrollbar_policy);
595 gtk_widget_set_size_request(scrolledwin,
596 prefs_common.folderview_width,
597 prefs_common.folderview_height);
599 folderview->scrolledwin = scrolledwin;
600 ctree = folderview_ctree_create(folderview);
602 /* create popup factories */
603 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
604 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
606 folderview->ctree = ctree;
608 folderview->folder_update_callback_id =
609 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
610 folderview->folder_item_update_callback_id =
611 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
613 gtk_widget_show_all(scrolledwin);
615 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
616 folderview_list = g_list_append(folderview_list, folderview);
618 folderview->drag_timer_id = 0;
619 folderview->deferred_refresh_id = 0;
620 folderview->scroll_timeout_id = 0;
621 folderview->postpone_select_id = 0;
626 void folderview_init(FolderView *folderview)
628 GtkWidget *ctree = folderview->ctree;
630 PangoFontDescription *normal_font;
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
670 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
671 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
672 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
673 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
674 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
675 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
676 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
677 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
679 normal_font = pango_font_description_from_string(NORMAL_FONT);
681 gtk_widget_modify_font(ctree, normal_font);
682 pango_font_description_free(normal_font);
684 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
687 PangoFontDescription *font_desc;
688 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
689 font_desc = pango_font_description_from_string(NORMAL_FONT);
691 if (normal_style->font_desc)
692 pango_font_description_free
693 (normal_style->font_desc);
694 normal_style->font_desc = font_desc;
696 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
697 normal_color_style = gtk_style_copy(normal_style);
698 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
702 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
703 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
704 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
705 PangoFontDescription *font_desc;
706 font_desc = pango_font_description_from_string(NORMAL_FONT);
708 pango_font_description_free(bold_style->font_desc);
709 bold_style->font_desc = font_desc;
711 pango_font_description_set_weight
712 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
714 PangoFontDescription *font_desc;
715 font_desc = pango_font_description_from_string(BOLD_FONT);
717 if (bold_style->font_desc)
718 pango_font_description_free
719 (bold_style->font_desc);
720 bold_style->font_desc = font_desc;
723 bold_color_style = gtk_style_copy(bold_style);
724 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
726 bold_tgtfold_style = gtk_style_copy(bold_style);
727 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
731 static gboolean folderview_defer_set(gpointer data)
733 FolderView *folderview = (FolderView *)data;
734 MainWindow *mainwin = folderview->mainwin;
738 if (mainwin->lock_count)
741 debug_print("doing deferred folderview_set now\n");
742 folderview_set(folderview);
744 folderview->deferred_refresh_id = 0;
748 void folderview_set(FolderView *folderview)
750 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
751 MainWindow *mainwin = folderview->mainwin;
752 FolderItem *sel_item = NULL, *op_item = NULL;
757 if (mainwin->lock_count) {
758 if (folderview->deferred_refresh_id == 0)
759 folderview->deferred_refresh_id =
760 g_timeout_add(500, folderview_defer_set, folderview);
761 debug_print("deferred folderview_set\n");
766 debug_print("Setting folder info...\n");
767 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
769 main_window_cursor_wait(mainwin);
771 if (folderview->selected)
772 sel_item = folderview_get_selected_item(folderview);
773 if (folderview->opened)
774 op_item = folderview_get_opened_item(folderview);
776 folderview->selected = NULL;
777 folderview->opened = NULL;
779 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
780 gtk_cmclist_clear(GTK_CMCLIST(ctree));
782 folderview_set_folders(folderview);
785 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
787 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
789 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
790 main_window_cursor_normal(mainwin);
791 STATUSBAR_POP(mainwin);
795 void folderview_set_all(void)
799 for (list = folderview_list; list != NULL; list = list->next)
800 folderview_set((FolderView *)list->data);
803 void folderview_select(FolderView *folderview, FolderItem *item)
805 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
806 GtkCMCTreeNode *node;
807 GtkCMCTreeNode *old_selected = folderview->selected;
811 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
812 if (node) folderview_select_node(folderview, node);
814 if (old_selected != node)
815 folder_update_op_count();
818 static void mark_all_read_cb(GtkAction *action, gpointer data)
820 mark_all_read_handler(action, data, FALSE);
823 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
825 mark_all_read_handler(action, data, TRUE);
828 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
830 FolderView *folderview = (FolderView *)data;
835 item = folderview_get_selected_item(folderview);
839 message = recursive? _("Do you really want to mark all mails in this "
840 "folder and its sub-folders as read?") :
841 _("Do you really want to mark all mails in this "
843 if (folderview->summaryview->folder_item != item &&
844 prefs_common.ask_mark_all_read) {
845 val = alertpanel_full(_("Mark all as read"),
846 message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
847 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
849 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
851 else if (val & G_ALERTDISABLE)
852 prefs_common.ask_mark_all_read = FALSE;
855 folder_item_update_freeze();
856 if (folderview->summaryview->folder_item != item && !recursive)
857 summary_lock(folderview->summaryview);
859 summary_freeze(folderview->summaryview);
862 folderutils_mark_all_read_recursive(item);
864 folderutils_mark_all_read(item);
866 if (folderview->summaryview->folder_item != item && !recursive)
867 summary_unlock(folderview->summaryview);
869 summary_thaw(folderview->summaryview);
870 folder_item_update_thaw();
873 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
875 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
877 cm_return_if_fail(node != NULL);
879 if (folderview->open_folder) {
883 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
884 gtkut_ctree_expand_parent_all(ctree, node);
886 folderview->open_folder = TRUE;
887 gtkut_ctree_set_focus_row(ctree, node);
888 gtk_cmctree_select(ctree, node);
889 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
890 if ((folderview->summaryview->folder_item &&
891 folderview->summaryview->folder_item->total_msgs > 0) ||
892 prefs_common.layout_mode == SMALL_LAYOUT)
893 summary_grab_focus(folderview->summaryview);
895 gtk_widget_grab_focus(folderview->ctree);
898 void folderview_unselect(FolderView *folderview)
900 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
902 (GTK_CMCTREE(folderview->ctree), folderview->opened);
904 folderview->selected = folderview->opened = NULL;
907 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
908 GtkCMCTreeNode *node,
914 node = gtkut_ctree_node_next(ctree, node);
916 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
918 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
919 item = gtk_cmctree_node_get_row_data(ctree, node);
922 if (item->stype == F_TRASH || item->stype == F_DRAFT)
926 if(item->unread_msgs > 0)
930 if(item->new_msgs > 0)
934 if(item->marked_msgs > 0)
943 void folderview_select_next_with_flag(FolderView *folderview,
947 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
948 GtkCMCTreeNode *node = NULL;
949 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
950 gboolean last_open = prefs_common.always_show_msg;
954 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
957 prefs_common.summary_select_prio[0] = ACTION_NEW;
960 prefs_common.summary_select_prio[0] = ACTION_MARKED;
963 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
965 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
967 folderview_select_node(folderview, node);
971 if (!folderview->opened ||
972 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
976 /* search again from the first node */
977 node = folderview_find_next_with_flag(ctree, NULL, flag);
979 folderview_select_node(folderview, node);
982 prefs_common.summary_select_prio[0] = last_summary_select_prio;
983 prefs_common.always_show_msg = last_open;
986 FolderItem *folderview_get_selected_item(FolderView *folderview)
988 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
990 if (!folderview->selected) return NULL;
991 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
994 FolderItem *folderview_get_opened_item(FolderView *folderview)
996 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
998 if (!folderview->opened) return NULL;
999 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1002 static void folderview_set_folders(FolderView *folderview)
1005 list = folder_get_list();
1007 for (; list != NULL; list = list->next) {
1008 folderview_append_folder(folderview, FOLDER(list->data));
1012 static gchar *get_scan_str(FolderItem *item)
1015 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1016 item->folder->name, G_DIR_SEPARATOR,
1019 return g_strdup_printf(_("Scanning folder %s..."),
1020 item->folder->name);
1022 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1026 for (list = folderview_list; list != NULL; list = list->next) {
1027 FolderView *folderview = (FolderView *)list->data;
1028 MainWindow *mainwin = folderview->mainwin;
1029 gchar *str = get_scan_str(item);
1031 STATUSBAR_PUSH(mainwin, str);
1032 STATUSBAR_POP(mainwin);
1037 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1040 MainWindow *mainwin = mainwindow_get_mainwindow();
1041 FolderView *folderview = NULL;
1042 GtkAdjustment *pos = NULL;
1045 cm_return_if_fail(folder != NULL);
1047 if (!folder->klass->scan_tree) return;
1050 alertpanel_full(_("Rebuild folder tree"),
1051 _("Rebuilding the folder tree will remove "
1052 "local caches. Do you want to continue?"),
1053 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1054 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1055 != G_ALERTALTERNATE) {
1061 window = label_window_create(_("Rebuilding folder tree..."));
1063 window = label_window_create(_("Scanning folder tree..."));
1066 folderview = mainwin->folderview;
1069 pos = gtk_scrolled_window_get_vadjustment(
1070 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1071 height = gtk_adjustment_get_value(pos);
1074 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1075 folder_scan_tree(folder, rebuild);
1076 folder_set_ui_func(folder, NULL, NULL);
1078 folderview_set_all();
1081 pos = gtk_scrolled_window_get_vadjustment(
1082 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1083 gtk_adjustment_set_value(pos, height);
1084 gtk_adjustment_changed(pos);
1086 label_window_destroy(window);
1090 /** folderview_check_new()
1091 * Scan and update the folder and return the
1092 * count the number of new messages since last check.
1093 * \param folder the folder to check for new messages
1094 * \return the number of new messages since last check
1096 gint folderview_check_new(Folder *folder)
1100 FolderView *folderview;
1102 GtkCMCTreeNode *node;
1104 gint former_new_msgs = 0;
1105 gint former_new = 0, former_unread = 0, former_total;
1107 for (list = folderview_list; list != NULL; list = list->next) {
1108 folderview = (FolderView *)list->data;
1109 ctree = GTK_CMCTREE(folderview->ctree);
1110 folderview->scanning_folder = folder;
1112 main_window_lock(folderview->mainwin);
1114 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1115 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1117 item = gtk_cmctree_node_get_row_data(ctree, node);
1118 if (!item || !item->path || !item->folder) continue;
1119 if (item->no_select) continue;
1120 if (folder && folder != item->folder) continue;
1121 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1122 if (!item->prefs->newmailcheck) continue;
1123 if (item->processing_pending == TRUE) {
1124 debug_print("skipping %s, processing pending\n",
1125 item->path ? item->path : item->name);
1128 if (item->scanning != ITEM_NOT_SCANNING) {
1129 debug_print("skipping %s, scanning\n",
1130 item->path ? item->path : item->name);
1134 str = get_scan_str(item);
1136 STATUSBAR_PUSH(folderview->mainwin, str);
1140 folderview_scan_tree_func(item->folder, item, NULL);
1141 former_new = item->new_msgs;
1142 former_unread = item->unread_msgs;
1143 former_total = item->total_msgs;
1145 if (item->folder->klass->scan_required &&
1146 (item->folder->klass->scan_required(item->folder, item) ||
1147 item->folder->inbox == item ||
1148 item->opened == TRUE ||
1149 item->processing_pending == TRUE)) {
1150 if (folder_item_scan(item) < 0) {
1152 summaryview_unlock(folderview->summaryview, item);
1153 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1154 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1155 item->path ? item->path:item->name);
1156 STATUSBAR_POP(folderview->mainwin);
1158 } else if (!FOLDER_IS_LOCAL(folder)) {
1159 STATUSBAR_POP(folderview->mainwin);
1164 } else if (!item->folder->klass->scan_required) {
1165 if (folder_item_scan(item) < 0) {
1166 summaryview_unlock(folderview->summaryview, item);
1167 if (folder && !FOLDER_IS_LOCAL(folder)) {
1168 STATUSBAR_POP(folderview->mainwin);
1173 if (former_new != item->new_msgs ||
1174 former_unread != item->unread_msgs ||
1175 former_total != item->total_msgs)
1176 folderview_update_node(folderview, node);
1178 new_msgs += item->new_msgs;
1179 former_new_msgs += former_new;
1180 STATUSBAR_POP(folderview->mainwin);
1182 folderview->scanning_folder = NULL;
1183 main_window_unlock(folderview->mainwin);
1187 folder_write_list();
1188 /* Number of new messages since last check is the just the difference
1189 * between former_new_msgs and new_msgs. If new_msgs is less than
1190 * former_new_msgs, that would mean another session accessed the folder
1191 * and the result is not well defined.
1193 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1197 void folderview_check_new_all(void)
1201 FolderView *folderview;
1203 folderview = (FolderView *)folderview_list->data;
1206 main_window_lock(folderview->mainwin);
1207 window = label_window_create
1208 (_("Checking for new messages in all folders..."));
1210 list = folder_get_list();
1211 for (; list != NULL; list = list->next) {
1212 Folder *folder = list->data;
1214 folderview_check_new(folder);
1217 folder_write_list();
1218 folderview_set_all();
1220 label_window_destroy(window);
1221 main_window_unlock(folderview->mainwin);
1225 static gboolean folderview_have_children_sub(FolderView *folderview,
1231 if (!item || !item->folder || !item->folder->node)
1234 node = item->folder->node;
1236 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1237 node = node->children;
1239 if (in_sub && item->total_msgs > 0) {
1243 while (node != NULL) {
1244 if (node && node->data) {
1245 FolderItem *next_item = (FolderItem*) node->data;
1247 if (folderview_have_children_sub(folderview,
1256 static gboolean folderview_have_children(FolderView *folderview,
1259 return folderview_have_children_sub(folderview, item, FALSE);
1262 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1268 if (!item || !item->folder || !item->folder->node)
1271 node = item->folder->node;
1273 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1274 node = node->children;
1277 (item->new_msgs > 0 ||
1278 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1282 while (node != NULL) {
1283 if (node && node->data) {
1284 FolderItem *next_item = (FolderItem*) node->data;
1286 if (folderview_have_new_children_sub(folderview,
1295 static gboolean folderview_have_new_children(FolderView *folderview,
1298 return folderview_have_new_children_sub(folderview, item, FALSE);
1301 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1307 if (!item || !item->folder || !item->folder->node)
1310 node = item->folder->node;
1312 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1313 node = node->children;
1316 (item->unread_msgs > 0 ||
1317 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1321 while (node != NULL) {
1322 if (node && node->data) {
1323 FolderItem *next_item = (FolderItem*) node->data;
1325 if (folderview_have_unread_children_sub(folderview,
1335 static gboolean folderview_have_unread_children(FolderView *folderview,
1338 return folderview_have_unread_children_sub(folderview, item, FALSE);
1341 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1347 if (!item || !item->folder || !item->folder->node)
1350 node = item->folder->node;
1352 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1353 node = node->children;
1355 if (in_sub && item->search_match){
1359 while (node != NULL) {
1360 if (node && node->data) {
1361 FolderItem *next_item = (FolderItem*) node->data;
1363 if (folderview_have_matching_children_sub(folderview,
1373 static gboolean folderview_have_matching_children(FolderView *folderview,
1376 return folderview_have_matching_children_sub(folderview, item, FALSE);
1379 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1385 if (!item || !item->folder || !item->folder->node)
1388 node = item->folder->node;
1390 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1391 node = node->children;
1393 if (item->marked_msgs != 0) {
1397 while (node != NULL) {
1398 if (node && node->data) {
1399 FolderItem *next_item = (FolderItem*) node->data;
1401 if (folderview_have_marked_children_sub(folderview,
1410 static gboolean folderview_have_marked_children(FolderView *folderview,
1413 return folderview_have_marked_children_sub(folderview, item, FALSE);
1416 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1418 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1419 GtkStyle *style = NULL;
1420 GtkStyle *color_style = NULL;
1422 GdkPixbuf *xpm, *openxpm;
1423 static GdkPixbuf *searchicon;
1424 gboolean mark = FALSE;
1427 gboolean add_unread_mark;
1428 gboolean add_sub_match_mark;
1429 gboolean use_bold, use_color;
1430 gint *col_pos = folderview->col_pos;
1431 SpecialFolderItemType stype;
1433 item = gtk_cmctree_node_get_row_data(ctree, node);
1434 cm_return_if_fail(item != NULL);
1436 if (!GTK_CMCTREE_ROW(node)->expanded)
1437 mark = folderview_have_marked_children(folderview, item);
1439 mark = (item->marked_msgs != 0);
1441 stype = item->stype;
1442 if (stype == F_NORMAL) {
1443 if (folder_has_parent_of_type(item, F_TRASH))
1445 else if (folder_has_parent_of_type(item, F_DRAFT))
1447 else if (folder_has_parent_of_type(item, F_OUTBOX))
1449 else if (folder_has_parent_of_type(item, F_QUEUE))
1454 if (item->hide_read_msgs || item->hide_read_threads) {
1455 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1456 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1458 xpm = mark?m_inboxxpm:inboxxpm;
1459 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1463 if (item->hide_read_msgs || item->hide_read_threads) {
1464 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1465 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1467 xpm = mark?m_outboxxpm:outboxxpm;
1468 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1472 if (item->hide_read_msgs || item->hide_read_threads) {
1473 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1474 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1476 xpm = mark?m_queuexpm:queuexpm;
1477 openxpm = mark?m_queueopenxpm:queueopenxpm;
1481 if (item->hide_read_msgs || item->hide_read_threads) {
1482 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1483 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1485 xpm = mark?m_trashxpm:trashxpm;
1486 openxpm = mark?m_trashopenxpm:trashopenxpm;
1490 xpm = mark?m_draftsxpm:draftsxpm;
1491 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1494 if (item->hide_read_msgs || item->hide_read_threads) {
1495 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1496 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1498 xpm = mark?m_folderxpm:folderxpm;
1499 openxpm = mark?m_folderopenxpm:folderopenxpm;
1503 if (item->no_select) {
1504 xpm = openxpm = noselectxpm;
1507 name = folder_item_get_name(item);
1509 if (!GTK_CMCTREE_ROW(node)->expanded) {
1510 add_unread_mark = folderview_have_unread_children(
1512 add_sub_match_mark = folderview_have_matching_children(
1515 add_unread_mark = FALSE;
1516 add_sub_match_mark = FALSE;
1519 if (item->search_match) {
1521 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1524 xpm = openxpm = searchicon;
1528 if (prefs_common.display_folder_unread) {
1529 if (folder_has_parent_of_type(item, F_QUEUE)) {
1530 /* only total_msgs matters here */
1531 if (item->total_msgs > 0) {
1532 /* show total number (should be equal to the unread number)
1534 str = g_strdup_printf("%s (%d%s%s)",
1535 name, item->total_msgs,
1536 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1537 (item->unreadmarked_msgs > 0) ? "!" : "");
1540 if (prefs_common.display_folder_unread == 1) {
1541 if (item->unread_msgs > 0) {
1542 /* show unread number and signs */
1543 str = g_strdup_printf("%s (%d%s%s)",
1544 name, item->unread_msgs,
1545 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1546 (item->unreadmarked_msgs > 0) ? "!" : "");
1549 if (item->total_msgs > 0) {
1550 /* show unread number, total number and signs if any */
1551 str = g_strdup_printf("%s (%d/%d%s%s)",
1552 name, item->unread_msgs, item->total_msgs,
1553 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1554 (item->unreadmarked_msgs > 0) ? "!" : "");
1558 if ((str == NULL) &&
1559 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1560 /* no unread/total numbers, but at least one sign */
1561 str = g_strdup_printf("%s (%s%s)",
1563 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1564 (item->unreadmarked_msgs > 0) ? "!" : "");
1568 /* last fallback, folder name only or with +! sign */
1569 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1570 str = g_strdup_printf("%s%s",
1572 } else if (item->unreadmarked_msgs > 0) {
1573 str = g_strdup_printf("%s%s",
1575 } else if (add_sub_match_mark) {
1576 str = g_strdup_printf("%s%s",
1579 str = g_strdup_printf("%s", name);
1582 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1584 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1588 if (!folder_item_parent(item)) {
1589 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1590 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1591 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1593 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1594 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1595 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1598 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1599 folder_has_parent_of_type(item, F_TRASH)) {
1600 use_bold = use_color = FALSE;
1601 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1602 GSList *list = folder_item_get_msg_list(item);
1604 use_bold = use_color = FALSE;
1605 for (cur = list; cur; cur = cur->next) {
1606 MsgInfo *msginfo = (MsgInfo *)cur->data;
1607 if (!MSG_IS_DELETED(msginfo->flags)) {
1608 /* highlight queue folder if there are any messages */
1609 use_bold = use_color = TRUE;
1613 if (!GTK_CMCTREE_ROW(node)->expanded &&
1614 use_bold == FALSE &&
1615 folderview_have_children(folderview, item))
1616 use_bold = use_color = TRUE;
1617 procmsg_msg_list_free(list);
1619 /* if unread messages exist, print with bold font */
1620 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1622 /* if new messages exist, print with colored letter */
1624 (item->new_msgs > 0) ||
1626 folderview_have_new_children(folderview, item));
1629 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1634 if (item->prefs->color > 0 && !use_color) {
1635 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1636 color_style = gtk_style_copy(bold_style);
1637 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1638 style = color_style;
1639 } else if (use_color) {
1640 style = bold_color_style;
1643 if (item->op_count > 0) {
1644 style = bold_tgtfold_style;
1646 } else if (use_color) {
1647 style = normal_color_style;
1648 gtk_cmctree_node_set_foreground(ctree, node,
1649 &folderview->color_new);
1650 } else if (item->op_count > 0) {
1651 style = bold_tgtfold_style;
1652 } else if (item->prefs->color > 0) {
1654 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1655 color_style = gtk_style_copy(normal_style);
1656 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1657 style = color_style;
1659 style = normal_style;
1662 gtk_cmctree_node_set_row_style(ctree, node, style);
1664 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1665 folderview_update_node(folderview, node);
1668 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1671 FolderView *folderview;
1673 GtkCMCTreeNode *node;
1675 cm_return_if_fail(item != NULL);
1677 for (list = folderview_list; list != NULL; list = list->next) {
1678 folderview = (FolderView *)list->data;
1679 ctree = GTK_CMCTREE(folderview->ctree);
1681 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1682 if (node && item->search_match != matches) {
1683 item->search_match = matches;
1684 folderview_update_node(folderview, node);
1689 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1691 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1692 FolderView *folderview = (FolderView *)data;
1694 GtkCMCTreeNode *node;
1695 cm_return_val_if_fail(update_info != NULL, TRUE);
1696 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1697 cm_return_val_if_fail(folderview != NULL, FALSE);
1699 ctree = GTK_CMCTREE(folderview->ctree);
1701 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1704 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1705 folderview_update_node(folderview, node);
1707 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1708 update_info->item == folderview->summaryview->folder_item &&
1709 update_info->item != NULL)
1710 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1711 summary_show(folderview->summaryview, update_info->item);
1717 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1718 GNode *gnode, GtkCMCTreeNode *cnode,
1721 FolderView *folderview = (FolderView *)data;
1722 FolderItem *item = FOLDER_ITEM(gnode->data);
1724 cm_return_val_if_fail(item != NULL, FALSE);
1726 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1727 folderview_update_node(folderview, cnode);
1732 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1735 FolderView *folderview = (FolderView *)data;
1738 if (GTK_CMCTREE_ROW(node)->children) {
1739 item = gtk_cmctree_node_get_row_data(ctree, node);
1740 cm_return_if_fail(item != NULL);
1742 if (!item->collapsed)
1743 gtk_cmctree_expand(ctree, node);
1745 folderview_update_node(folderview, node);
1749 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1750 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1753 GtkCMCTreeNode *node, *parent, *sibling;
1755 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1757 g_warning("%s not found.", item->path);
1759 parent = GTK_CMCTREE_ROW(node)->parent;
1760 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1761 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1763 sibling = GTK_CMCTREE_ROW(parent)->children;
1767 tmp = gtk_cmctree_node_get_row_data
1769 if (tmp && tmp->stype != F_NORMAL)
1770 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1774 if (node != sibling)
1775 gtk_cmctree_move(ctree, node, parent, sibling);
1782 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1785 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1786 GtkCMCTreeNode *prev = NULL;
1788 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1789 gtk_sctree_sort_recursive(ctree, root);
1790 if (root && GTK_CMCTREE_ROW(root)->parent) {
1791 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1794 set_special_folder(ctree, folder->inbox, root, &prev);
1795 set_special_folder(ctree, folder->outbox, root, &prev);
1796 set_special_folder(ctree, folder->draft, root, &prev);
1797 set_special_folder(ctree, folder->queue, root, &prev);
1798 set_special_folder(ctree, folder->trash, root, &prev);
1799 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1802 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1804 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1805 GtkCMCTreeNode *root;
1807 cm_return_if_fail(folder != NULL);
1809 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1810 folderview_gnode_func, folderview);
1811 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1813 folderview_sort_folders(folderview, root, folder);
1816 /* callback functions */
1817 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1818 GdkEventButton *event)
1822 FolderViewPopup *fpopup;
1823 GtkActionGroup *action_group;
1825 FolderItem *special_trash = NULL, *special_queue = NULL;
1827 GtkUIManager *ui_manager = gtk_ui_manager_new();
1829 if (folderview->ui_manager)
1830 g_object_unref(folderview->ui_manager);
1832 folderview->ui_manager = ui_manager;
1833 item = folderview_get_selected_item(folderview);
1835 cm_return_if_fail(item != NULL);
1836 cm_return_if_fail(item->folder != NULL);
1837 folder = item->folder;
1839 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1842 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1844 fpopup = g_hash_table_lookup(folderview_popups, "common");
1845 action_group = g_hash_table_lookup(folderview->popups, "common");
1848 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1850 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1852 if (fpopup->add_menuitems)
1853 fpopup->add_menuitems(ui_manager, item);
1855 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1856 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1857 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1858 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1859 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1860 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1861 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1863 if (fpopup->set_sensitivity != NULL)
1864 fpopup->set_sensitivity(ui_manager, item);
1866 if (NULL != (ac = account_find_from_item(item))) {
1867 special_trash = account_get_special_folder(ac, F_TRASH);
1868 special_queue = account_get_special_folder(ac, F_QUEUE);
1871 if ((item == folder->trash || item == special_trash
1872 || folder_has_parent_of_type(item, F_TRASH))) {
1873 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1874 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1877 if ((item == folder->queue || item == special_queue
1878 || folder_has_parent_of_type(item, F_QUEUE))) {
1879 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1880 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1883 #define SET_SENS(name, sens) \
1884 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1886 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1887 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1888 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1889 folderview->selected == folderview->opened);
1890 SET_SENS("FolderViewPopup/Properties", TRUE);
1892 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1893 item->total_msgs >= 1 && !item->processing_pending);
1894 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1895 !item->no_select && !item->processing_pending);
1897 if (item == folder->trash || item == special_trash
1898 || folder_has_parent_of_type(item, F_TRASH)) {
1899 GSList *msglist = folder_item_get_msg_list(item);
1900 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1901 procmsg_msg_list_free(msglist);
1903 if (item == folder->queue || item == special_queue
1904 || folder_has_parent_of_type(item, F_QUEUE)) {
1905 GSList *msglist = folder_item_get_msg_list(item);
1906 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1907 procmsg_msg_list_free(msglist);
1911 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1912 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1913 g_signal_connect(G_OBJECT(popup), "selection_done",
1914 G_CALLBACK(folderview_popup_close),
1916 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1917 event->button, event->time);
1920 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1921 FolderView *folderview)
1923 GtkCMCList *clist = GTK_CMCLIST(ctree);
1924 gint prev_row = -1, row = -1, column = -1;
1926 if (!event) return FALSE;
1928 if (event->button == 1 || event->button == 2) {
1929 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1930 folderview->open_folder = TRUE;
1932 if (event->type == GDK_2BUTTON_PRESS) {
1933 if (clist->selection) {
1934 GtkCMCTreeNode *node;
1936 node = GTK_CMCTREE_NODE(clist->selection->data);
1938 gtk_cmctree_toggle_expansion(
1941 folderview->open_folder = FALSE;
1948 if (event->button == 2 || event->button == 3) {
1950 if (clist->selection) {
1951 GtkCMCTreeNode *node;
1953 node = GTK_CMCTREE_NODE(clist->selection->data);
1955 prev_row = gtkut_ctree_get_nth_from_node
1956 (GTK_CMCTREE(ctree), node);
1959 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1962 if (prev_row != row) {
1963 gtk_cmclist_unselect_all(clist);
1964 if (event->button == 2)
1965 folderview_select_node
1967 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1970 gtk_cmclist_select_row(clist, row, column);
1974 if (event->button != 3) return FALSE;
1976 folderview_set_sens_and_popup_menu(folderview, row, event);
1980 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1981 FolderView *folderview)
1983 int row = -1, column = -1;
1985 if (!event) return FALSE;
1987 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1990 if (event->button == 1 && folderview->open_folder == FALSE &&
1991 folderview->opened != NULL) {
1992 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1993 folderview->opened);
1994 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2000 #define BREAK_ON_MODIFIER_KEY() \
2001 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2003 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2004 FolderView *folderview)
2006 GtkCMCTreeNode *node;
2009 if (!event) return FALSE;
2011 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2014 switch (event->keyval) {
2016 if (folderview->selected) {
2017 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2018 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2019 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2020 folderview->selected);
2022 folderview_select_node(folderview,
2023 folderview->selected);
2027 case GDK_KEY_Return:
2028 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2029 gtk_cmctree_toggle_expansion(
2030 GTK_CMCTREE(folderview->ctree),
2031 folderview->selected);
2035 case GDK_KEY_Return:
2036 case GDK_KEY_KP_Enter:
2037 if (folderview->selected)
2038 folderview_select_node(folderview, folderview->selected);
2042 BREAK_ON_MODIFIER_KEY();
2043 if (folderview->selected) {
2044 if (folderview->opened == folderview->selected &&
2045 (!folderview->summaryview->folder_item ||
2046 folderview->summaryview->folder_item->total_msgs == 0))
2047 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2049 folderview_select_node(folderview,
2050 folderview->selected);
2054 if (folderview->selected) {
2055 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2056 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2057 folderview->selected);
2059 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2060 folderview->selected))) {
2061 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2062 NULL, folder_item_parent(item)))) {
2063 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2064 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2065 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2074 if (event->keyval == GDK_KEY_Home)
2075 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2077 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2078 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2080 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2082 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2083 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2093 typedef struct _PostponedSelectData
2096 GtkCMCTreeNode *row;
2098 FolderView *folderview;
2099 } PostponedSelectData;
2101 static gboolean postpone_select(void *data)
2103 PostponedSelectData *psdata = (PostponedSelectData *)data;
2104 debug_print("trying again\n");
2106 psdata->folderview->postpone_select_id = 0;
2107 psdata->folderview->open_folder = TRUE;
2108 main_window_cursor_normal(psdata->folderview->mainwin);
2109 STATUSBAR_POP(psdata->folderview->mainwin);
2110 folderview_selected(psdata->ctree, psdata->row,
2111 psdata->column, psdata->folderview);
2116 void folderview_close_opened(FolderView *folderview)
2118 if (folderview->opened) {
2119 FolderItem *olditem;
2121 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2122 folderview->opened);
2124 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2125 olditem->path ? olditem->path:olditem->name);
2126 /* will be null if we just moved the previously opened folder */
2127 STATUSBAR_PUSH(folderview->mainwin, buf);
2128 main_window_cursor_wait(folderview->mainwin);
2130 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2131 summary_show(folderview->summaryview, NULL);
2132 folder_item_close(olditem);
2133 main_window_cursor_normal(folderview->mainwin);
2134 STATUSBAR_POP(folderview->mainwin);
2135 if (olditem->folder->klass->item_closed)
2136 olditem->folder->klass->item_closed(olditem);
2141 if (folderview->opened &&
2142 !GTK_CMCTREE_ROW(folderview->opened)->children)
2143 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2145 folderview->opened = NULL;
2147 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2148 gint column, FolderView *folderview)
2150 static gboolean can_select = TRUE; /* exclusive lock */
2155 GtkCMCTreeNode *old_opened = folderview->opened;
2157 folderview->selected = row;
2159 debug_print("newly selected %p, opened %p\n", folderview->selected,
2160 folderview->opened);
2161 if (folderview->opened == row) {
2162 folderview->open_folder = FALSE;
2167 item = gtk_cmctree_node_get_row_data(ctree, row);
2170 folderview->open_folder = FALSE;
2174 if (!can_select || summary_is_locked(folderview->summaryview)) {
2175 if (folderview->opened) {
2176 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2177 gtk_cmctree_select(ctree, folderview->opened);
2179 folderview->open_folder = FALSE;
2184 if (!folderview->open_folder) {
2191 /* Save cache for old folder */
2192 /* We don't want to lose all caches if sylpheed crashed */
2193 /* resets folderview->opened to NULL */
2194 folderview_close_opened(folderview);
2196 /* CLAWS: set compose button type: news folder items
2197 * always have a news folder as parent */
2199 toolbar_set_compose_button
2200 (folderview->mainwin->toolbar,
2201 FOLDER_TYPE(item->folder) == F_NEWS ?
2202 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2205 debug_print("Folder %s is selected\n", item->path);
2207 if (!GTK_CMCTREE_ROW(row)->children)
2208 gtk_cmctree_expand(ctree, row);
2210 /* ungrab the mouse event */
2211 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2212 gtk_grab_remove(GTK_WIDGET(ctree));
2213 if (gdk_pointer_is_grabbed())
2214 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2218 /* TODO: wwp: avoid displaying (null) in the status bar */
2219 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2220 item->path : "(null)");
2221 debug_print("%s\n", buf);
2222 STATUSBAR_PUSH(folderview->mainwin, buf);
2225 main_window_cursor_wait(folderview->mainwin);
2227 if (folderview->scanning_folder == item->folder) {
2230 res = folder_item_open(item);
2233 if (res == -1 && item->no_select == FALSE) {
2234 main_window_cursor_normal(folderview->mainwin);
2235 STATUSBAR_POP(folderview->mainwin);
2237 alertpanel_error(_("Folder could not be opened."));
2239 folderview->open_folder = FALSE;
2243 } else if (res == -2 && item->no_select == FALSE) {
2244 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2245 data->ctree = ctree;
2247 data->column = column;
2248 data->folderview = folderview;
2249 debug_print("postponing open of %s till end of scan\n",
2250 item->path ? item->path:item->name);
2251 folderview->open_folder = FALSE;
2253 if (folderview->postpone_select_id != 0)
2254 g_source_remove(folderview->postpone_select_id);
2255 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2260 main_window_cursor_normal(folderview->mainwin);
2263 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2264 opened = summary_show(folderview->summaryview, item);
2266 folder_clean_cache_memory(item);
2269 gtkut_ctree_set_focus_row(ctree, old_opened);
2270 gtk_cmctree_select(ctree, old_opened);
2271 folderview->opened = old_opened;
2273 folderview->opened = row;
2274 if (gtk_cmctree_node_is_visible(ctree, row)
2275 != GTK_VISIBILITY_FULL)
2276 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2279 STATUSBAR_POP(folderview->mainwin);
2281 folderview->open_folder = FALSE;
2286 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2287 FolderView *folderview)
2291 item = gtk_cmctree_node_get_row_data(ctree, node);
2292 cm_return_if_fail(item != NULL);
2293 item->collapsed = FALSE;
2294 folderview_update_node(folderview, node);
2297 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2298 FolderView *folderview)
2302 item = gtk_cmctree_node_get_row_data(ctree, node);
2303 cm_return_if_fail(item != NULL);
2304 item->collapsed = TRUE;
2305 folderview_update_node(folderview, node);
2308 static void folderview_popup_close(GtkMenuShell *menu_shell,
2309 FolderView *folderview)
2311 if (!folderview->opened) return;
2313 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2316 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2317 FolderView *folderview)
2319 FolderColumnType type = folderview->col_state[column].type;
2321 prefs_common.folder_col_size[type] = width;
2324 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2326 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2327 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2328 GtkCMCTreeNode *node, *parent_node;
2329 gint *col_pos = folderview->col_pos;
2330 FolderItemUpdateData hookdata;
2332 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2333 if (parent_node == NULL)
2336 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2338 text[col_pos[F_COL_FOLDER]] = item->name;
2339 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2344 gtk_cmctree_expand(ctree, parent_node);
2345 gtk_cmctree_node_set_row_data(ctree, node, item);
2347 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2348 folderview_sort_folders(folderview, parent_node, item->folder);
2350 hookdata.item = item;
2351 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2352 hookdata.msg = NULL;
2353 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2355 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2358 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2360 FolderView *folderview = (FolderView *)data;
2362 GSList *mlist = NULL;
2364 FolderItem *special_trash = NULL;
2367 if (!folderview->selected) return;
2368 item = folderview_get_selected_item(folderview);
2369 cm_return_if_fail(item != NULL);
2370 cm_return_if_fail(item->folder != NULL);
2372 if (NULL != (ac = account_find_from_item(item)))
2373 special_trash = account_get_special_folder(ac, F_TRASH);
2375 if (item != item->folder->trash && item != special_trash
2376 && !folder_has_parent_of_type(item, F_TRASH)) return;
2378 if (prefs_common.ask_on_clean) {
2379 if (alertpanel(_("Empty trash"),
2380 _("Delete all messages in trash?"),
2381 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2385 mlist = folder_item_get_msg_list(item);
2387 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2388 MsgInfo * msginfo = (MsgInfo *) cur->data;
2389 if (MSG_IS_LOCKED(msginfo->flags))
2391 /* is it partially received? (partial_recv isn't cached) */
2392 if (msginfo->total_size != 0 &&
2393 msginfo->size != (off_t)msginfo->total_size)
2394 partial_mark_for_delete(msginfo);
2396 procmsg_msg_list_free(mlist);
2398 folder_item_remove_all_msg(item);
2401 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2403 FolderView *folderview = (FolderView *)data;
2405 FolderItem *special_queue = NULL;
2407 gchar *errstr = NULL;
2409 if (!folderview->selected) return;
2410 item = folderview_get_selected_item(folderview);
2411 cm_return_if_fail(item != NULL);
2412 cm_return_if_fail(item->folder != NULL);
2414 if (NULL != (ac = account_find_from_item(item)))
2415 special_queue = account_get_special_folder(ac, F_QUEUE);
2417 if (item != item->folder->queue && item != special_queue
2418 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2420 if (procmsg_queue_is_empty(item))
2423 if (prefs_common.work_offline)
2424 if (alertpanel(_("Offline warning"),
2425 _("You're working offline. Override?"),
2426 GTK_STOCK_NO, GTK_STOCK_YES,
2427 NULL) != G_ALERTALTERNATE)
2430 /* ask for confirmation before sending queued messages only
2431 in online mode and if there is at least one message queued
2432 in any of the folder queue
2434 if (prefs_common.confirm_send_queued_messages) {
2435 if (!prefs_common.work_offline) {
2436 if (alertpanel(_("Send queued messages"),
2437 _("Send all queued messages?"),
2438 GTK_STOCK_CANCEL, _("_Send"),
2439 NULL) != G_ALERTALTERNATE)
2444 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2446 alertpanel_error_log(_("Some errors occurred while "
2447 "sending queued messages."));
2449 alertpanel_error_log(_("Some errors occurred "
2450 "while sending queued messages:\n%s"), errstr);
2456 static void folderview_search_cb(GtkAction *action, gpointer data)
2458 FolderView *folderview = (FolderView *)data;
2459 summary_search(folderview->summaryview);
2462 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2464 FolderView *folderview = (FolderView *)data;
2467 if (!folderview->selected) return;
2469 item = folderview_get_selected_item(folderview);
2470 cm_return_if_fail(item != NULL);
2471 cm_return_if_fail(item->folder != NULL);
2473 item->processing_pending = TRUE;
2474 folder_item_apply_processing(item);
2475 item->processing_pending = FALSE;
2478 static void folderview_property_cb(GtkAction *action, gpointer data)
2480 FolderView *folderview = (FolderView *)data;
2483 if (!folderview->selected) return;
2485 item = folderview_get_selected_item(folderview);
2486 cm_return_if_fail(item != NULL);
2487 cm_return_if_fail(item->folder != NULL);
2489 prefs_folder_item_open(item);
2492 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2494 GSList *list = NULL;
2495 GSList *done = NULL;
2496 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2498 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2499 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2500 && list->data != node) {
2501 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2502 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2505 for (list = done; list != NULL; list = g_slist_next(list)) {
2506 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2512 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2513 FolderItem *to_folder, gboolean copy)
2515 FolderItem *new_folder = NULL;
2519 cm_return_if_fail(folderview != NULL);
2520 cm_return_if_fail(from_folder != NULL);
2521 cm_return_if_fail(to_folder != NULL);
2523 if (prefs_common.warn_dnd) {
2524 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2525 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2526 from_folder->name, to_folder->name);
2527 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2528 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2529 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2532 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2534 else if (status & G_ALERTDISABLE)
2535 prefs_common.warn_dnd = FALSE;
2538 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2539 from_folder->name, to_folder->name);
2540 STATUSBAR_PUSH(folderview->mainwin, buf);
2542 summary_clear_all(folderview->summaryview);
2543 folderview->opened = NULL;
2544 folderview->selected = NULL;
2545 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2547 main_window_cursor_wait(folderview->mainwin);
2549 statusbar_verbosity_set(FALSE);
2550 folder_item_update_freeze();
2551 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2552 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2553 statusbar_verbosity_set(FALSE);
2554 main_window_cursor_normal(folderview->mainwin);
2555 STATUSBAR_POP(folderview->mainwin);
2556 folder_item_update_thaw();
2557 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2559 folderview_sort_folders(folderview,
2560 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2561 NULL, to_folder), new_folder->folder);
2562 folderview_select(folderview, new_folder);
2563 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2565 statusbar_verbosity_set(FALSE);
2566 main_window_cursor_normal(folderview->mainwin);
2567 STATUSBAR_POP(folderview->mainwin);
2568 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2569 folder_item_update_thaw();
2571 case F_MOVE_FAILED_DEST_IS_PARENT:
2572 alertpanel_error(_("Source and destination are the same."));
2574 case F_MOVE_FAILED_DEST_IS_CHILD:
2575 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2576 _("Can't move a folder to one of its children."));
2578 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2579 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2582 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2587 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2590 static gint folderview_clist_compare(GtkCMCList *clist,
2591 gconstpointer ptr1, gconstpointer ptr2)
2593 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2594 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2596 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2598 return item1->order - item2->order;
2601 // if only one folder has an order it comes first
2602 if (item1->order > 0)
2606 if (item2->order > 0)
2612 return (item2->name != NULL);
2616 return g_utf8_collate(item1->name, item2->name);
2619 static void folderview_processing_cb(GtkAction *action, gpointer data)
2621 FolderView *folderview = (FolderView *)data;
2625 if (!folderview->selected) return;
2627 item = folderview_get_selected_item(folderview);
2628 cm_return_if_fail(item != NULL);
2629 cm_return_if_fail(item->folder != NULL);
2631 id = folder_item_get_identifier(item);
2632 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2635 prefs_filtering_open(&item->prefs->processing, title,
2636 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2640 void folderview_set_target_folder_color(gint color_op)
2644 FolderView *folderview;
2646 for (list = folderview_list; list != NULL; list = list->next) {
2647 folderview = (FolderView *)list->data;
2648 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2650 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2651 folderview->color_op;
2657 static gchar *last_smallfont = NULL;
2658 static gchar *last_normalfont = NULL;
2659 static gchar *last_boldfont = NULL;
2660 static gboolean last_derive = 0;
2662 void folderview_reinit_fonts(FolderView *folderview)
2665 g_free(last_smallfont);
2666 last_smallfont = NULL;
2667 g_free(last_normalfont);
2668 last_normalfont = NULL;
2669 g_free(last_boldfont);
2670 last_boldfont = NULL;
2673 void folderview_reflect_prefs(void)
2675 gboolean update_font = FALSE;
2676 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2677 FolderItem *item = folderview_get_selected_item(folderview);
2678 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2679 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2680 gint height = gtk_adjustment_get_value(pos);
2682 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2683 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2684 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2685 last_derive != prefs_common.derive_from_normal_font)
2688 g_free(last_smallfont);
2689 last_smallfont = g_strdup(SMALL_FONT);
2690 g_free(last_normalfont);
2691 last_normalfont = g_strdup(NORMAL_FONT);
2692 g_free(last_boldfont);
2693 last_boldfont = g_strdup(BOLD_FONT);
2694 last_derive = prefs_common.derive_from_normal_font;
2697 normal_style = normal_color_style = bold_style =
2698 bold_color_style = bold_tgtfold_style = NULL;
2700 folderview_init(folderview);
2702 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2703 folderview_column_set_titles(folderview);
2704 folderview_set_all();
2706 g_signal_handlers_block_by_func
2707 (G_OBJECT(folderview->ctree),
2708 G_CALLBACK(folderview_selected), folderview);
2711 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2712 GTK_CMCTREE(folderview->ctree), NULL, item);
2714 folderview_select(folderview, item);
2715 folderview->open_folder = FALSE;
2716 folderview->selected = node;
2719 g_signal_handlers_unblock_by_func
2720 (G_OBJECT(folderview->ctree),
2721 G_CALLBACK(folderview_selected), folderview);
2723 pos = gtk_scrolled_window_get_vadjustment(
2724 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2725 gtk_adjustment_set_value(pos, height);
2726 gtk_adjustment_changed(pos);
2727 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2730 static void drag_state_stop(FolderView *folderview)
2732 if (folderview->drag_timer_id)
2733 g_source_remove(folderview->drag_timer_id);
2734 folderview->drag_timer_id = 0;
2735 folderview->drag_node = NULL;
2738 static gboolean folderview_defer_expand(FolderView *folderview)
2740 if (folderview->drag_node) {
2741 folderview_recollapse_nodes(folderview, folderview->drag_node);
2742 if (folderview->drag_item->collapsed) {
2743 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2744 folderview->nodes_to_recollapse = g_slist_append
2745 (folderview->nodes_to_recollapse, folderview->drag_node);
2748 folderview->drag_item = NULL;
2749 folderview->drag_timer_id = 0;
2753 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2755 /* the idea is that we call drag_state_start() whenever we want expansion to
2756 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2757 * we need to call drag_state_stop() */
2758 drag_state_stop(folderview);
2759 /* request expansion */
2760 if (0 != (folderview->drag_timer_id = g_timeout_add
2761 (prefs_common.hover_timeout,
2762 (GSourceFunc)folderview_defer_expand,
2764 folderview->drag_node = node;
2765 folderview->drag_item = item;
2768 #ifndef GENERIC_UMPC
2769 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2770 FolderView *folderview)
2772 GdkDragContext *context;
2774 cm_return_if_fail(folderview != NULL);
2775 if (folderview->selected == NULL) return;
2776 if (folderview->nodes_to_recollapse)
2777 g_slist_free(folderview->nodes_to_recollapse);
2778 folderview->nodes_to_recollapse = NULL;
2779 context = gtk_drag_begin(widget, folderview->target_list,
2780 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2781 gtk_drag_set_icon_default(context);
2784 static void folderview_drag_data_get(GtkWidget *widget,
2785 GdkDragContext *drag_context,
2786 GtkSelectionData *selection_data,
2789 FolderView *folderview)
2793 gchar *source = NULL;
2794 if (info == TARGET_DUMMY) {
2795 sel = GTK_CMCLIST(folderview->ctree)->selection;
2799 item = gtk_cmctree_node_get_row_data
2800 (GTK_CMCTREE(folderview->ctree),
2801 GTK_CMCTREE_NODE(sel->data));
2803 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2804 gtk_selection_data_set(selection_data,
2805 gtk_selection_data_get_target(selection_data), 8,
2806 source, strlen(source));
2809 g_warning("unknown info %d", info);
2813 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2815 FolderUpdateData *hookdata;
2816 FolderView *folderview;
2820 folderview = (FolderView *) userdata;
2821 cm_return_val_if_fail(hookdata != NULL, FALSE);
2822 cm_return_val_if_fail(folderview != NULL, FALSE);
2824 ctree = folderview->ctree;
2825 cm_return_val_if_fail(ctree != NULL, FALSE);
2827 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2828 folderview_create_folder_node(folderview, hookdata->item);
2829 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2830 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2831 NULL, folder_item_parent(hookdata->item));
2832 folderview_sort_folders(folderview, node, hookdata->folder);
2833 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2834 GtkCMCTreeNode *node;
2836 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2838 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2839 if (folderview->selected == node)
2840 folderview->selected = NULL;
2841 if (folderview->opened == node)
2842 folderview->opened = NULL;
2844 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2845 /* do nothing, it's done by the ADD and REMOVE) */
2846 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2847 folderview_set(folderview);
2852 static gboolean folderview_dnd_scroll_cb(gpointer data)
2854 FolderView *folderview = (FolderView *)data;
2855 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2856 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2857 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2858 gint max = (int)gtk_adjustment_get_upper(pos) -
2859 (int)gtk_adjustment_get_page_size(pos);
2861 if (folderview->scroll_value == 0) {
2862 folderview->scroll_timeout_id = 0;
2866 if (folderview->scroll_value > 0 && new_val > max) {
2868 } else if (folderview->scroll_value < 0 && new_val < 0) {
2871 gtk_adjustment_set_value(pos, new_val);
2876 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2877 GdkDragContext *context,
2881 FolderView *folderview)
2884 FolderItem *item = NULL, *src_item = NULL;
2885 GtkCMCTreeNode *node = NULL;
2886 gboolean acceptable = FALSE;
2887 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2888 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2889 int height = (int)gtk_adjustment_get_page_size(pos);
2890 int total_height = (int)gtk_adjustment_get_upper(pos);
2891 int vpos = (int)gtk_adjustment_get_value(pos);
2892 int offset = prefs_common.show_col_headers ? 24:0;
2895 if (gtk_cmclist_get_selection_info
2896 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2897 GtkWidget *srcwidget;
2899 if (y > height - (48 - offset) && height + vpos < total_height) {
2900 dist = -(height - (48 - offset) - y);
2901 folderview->scroll_value = 1.41f * (1+(dist / 6));
2902 } else if (y < 72 - (24 - offset) && y >= 0) {
2903 dist = 72 - (24 - offset) - y;
2904 folderview->scroll_value = -1.41f * (1+(dist / 6));
2906 folderview->scroll_value = 0;
2908 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2909 folderview->scroll_timeout_id =
2910 g_timeout_add(30, folderview_dnd_scroll_cb,
2914 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2915 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2916 src_item = folderview->summaryview->folder_item;
2918 srcwidget = gtk_drag_get_source_widget(context);
2919 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2920 /* comes from summaryview */
2921 /* we are copying messages, so only accept folder items that are not
2922 the source item, are no root items and can copy messages */
2923 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2924 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2925 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2927 } else if (srcwidget == folderview->ctree) {
2928 /* comes from folderview */
2929 /* we are moving folder items, only accept folders that are not
2930 the source items and can copy messages and create folder items */
2931 if (item && item->folder && src_item && src_item != item &&
2932 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2933 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2934 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2935 || item->folder == src_item->folder))
2938 /* comes from another app */
2939 /* we are adding messages, so only accept folder items that are
2940 no root items and can copy messages */
2941 if (item && item->folder && folder_item_parent(item) != NULL
2942 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2943 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2948 if (acceptable || (src_item && src_item == item))
2949 drag_state_start(folderview, node, item);
2952 g_signal_handlers_block_by_func
2954 G_CALLBACK(folderview_selected), folderview);
2955 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2956 g_signal_handlers_unblock_by_func
2958 G_CALLBACK(folderview_selected), folderview);
2959 gdk_drag_status(context,
2960 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2961 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2963 if (folderview->opened)
2964 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2965 gdk_drag_status(context, 0, time);
2971 static void folderview_drag_leave_cb(GtkWidget *widget,
2972 GdkDragContext *context,
2974 FolderView *folderview)
2976 drag_state_stop(folderview);
2977 folderview->scroll_value = 0;
2978 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2981 static void free_info (gpointer stuff, gpointer data)
2986 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2987 guint time, FolderItem *item)
2990 GSList *msglist = NULL;
2991 list = uri_list_extract_filenames(data);
2992 if (!(item && item->folder && folder_item_parent(item) != NULL
2993 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2995 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2996 debug_print("item doesn't fit\n");
3000 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3001 debug_print("list is empty\n");
3004 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3005 MsgFileInfo *info = NULL;
3007 if (file_is_email((gchar *)tmp->data)) {
3008 info = g_new0(MsgFileInfo, 1);
3009 info->msginfo = NULL;
3010 info->file = (gchar *)tmp->data;
3011 msglist = g_slist_prepend(msglist, info);
3012 debug_print("file is a mail\n");
3014 debug_print("file isn't a mail\n");
3018 msglist = g_slist_reverse(msglist);
3019 folder_item_add_msgs(item, msglist, FALSE);
3020 g_slist_foreach(msglist, free_info, NULL);
3021 g_slist_free(msglist);
3022 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3024 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3026 list_free_strings(list);
3030 static void folderview_drag_received_cb(GtkWidget *widget,
3031 GdkDragContext *drag_context,
3034 GtkSelectionData *data,
3037 FolderView *folderview)
3040 FolderItem *item = NULL, *src_item;
3041 GtkCMCTreeNode *node;
3042 int offset = prefs_common.show_col_headers ? 24:0;
3044 folderview->scroll_value = 0;
3046 if (info == TARGET_DUMMY) {
3047 drag_state_stop(folderview);
3048 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3049 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3050 /* comes from summaryview */
3051 if (gtk_cmclist_get_selection_info
3052 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
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 = folderview->summaryview->folder_item;
3059 if (item->no_select) {
3060 alertpanel_error(_("The destination folder can only be used to "
3061 "store subfolders."));
3064 /* re-check (due to acceptable possibly set for folder moves */
3065 if (!(item && item->folder && item->path && !item->no_select &&
3066 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3069 if (item && src_item) {
3070 switch (gdk_drag_context_get_selected_action(drag_context)) {
3071 case GDK_ACTION_COPY:
3072 summary_copy_selected_to(folderview->summaryview, item);
3073 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3075 case GDK_ACTION_MOVE:
3076 case GDK_ACTION_DEFAULT:
3078 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3079 summary_copy_selected_to(folderview->summaryview, item);
3081 summary_move_selected_to(folderview->summaryview, item);
3082 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3085 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3087 /* comes from folderview */
3089 gboolean folder_is_normal = TRUE;
3090 gboolean copy = (GDK_ACTION_COPY ==
3091 gdk_drag_context_get_selected_action(drag_context));
3093 source = (char *)gtk_selection_data_get_data(data) + 17;
3094 if (gtk_cmclist_get_selection_info
3095 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3097 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3100 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3101 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3102 src_item = folder_find_item_from_identifier(source);
3106 src_item->stype == F_NORMAL &&
3107 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3108 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3109 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3110 !folder_has_parent_of_type(src_item, F_TRASH);
3111 if (!item || !src_item || !folder_is_normal) {
3112 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3116 folderview_move_folder(folderview, src_item, item, copy);
3117 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3119 folderview->nodes_to_recollapse = NULL;
3120 } else if (info == TARGET_MAIL_URI_LIST) {
3121 if (gtk_cmclist_get_selection_info
3122 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3125 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3127 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3128 debug_print("no node\n");
3131 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3133 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3134 debug_print("no item\n");
3137 folderview_finish_dnd(gtk_selection_data_get_data(data),
3138 drag_context, time, item);
3142 static void folderview_drag_end_cb(GtkWidget *widget,
3143 GdkDragContext *drag_context,
3144 FolderView *folderview)
3146 drag_state_stop(folderview);
3147 folderview->scroll_value = 0;
3148 g_slist_free(folderview->nodes_to_recollapse);
3149 folderview->nodes_to_recollapse = NULL;
3152 void folderview_register_popup(FolderViewPopup *fpopup)
3156 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3157 FolderView *folderview = folderviews->data;
3158 GtkActionGroup *factory;
3160 factory = create_action_group(folderview, fpopup);
3161 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3163 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3166 void folderview_unregister_popup(FolderViewPopup *fpopup)
3171 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3172 FolderView *folderview = folderviews->data;
3174 g_hash_table_remove(folderview->popups, fpopup->klass);
3176 g_hash_table_remove(folderview_popups, fpopup->klass);